From b7ab3fd96480c96e608e8c990b3b2f602ba4bb75 Mon Sep 17 00:00:00 2001 From: Clatlan Date: Fri, 23 Feb 2024 16:22:51 +0100 Subject: [PATCH 1/3] update the default value of parameter debug to False --- cdiutils/examples/analyze_bcdi_data.ipynb | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cdiutils/examples/analyze_bcdi_data.ipynb b/cdiutils/examples/analyze_bcdi_data.ipynb index 802d6dae..c608c0a5 100644 --- a/cdiutils/examples/analyze_bcdi_data.ipynb +++ b/cdiutils/examples/analyze_bcdi_data.ipynb @@ -63,9 +63,9 @@ " \"reconstruction_file\": \"mode.h5\"\n", "}\n", "\n", - "# You choose, either you specify it in the metadata, or you want\n", - "# the dump_dir to be dependent on the 'sample_name' and 'scan' or other\n", - "# things.\n", + "# Required, you choose, either you specify it in the metadata, or you\n", + "# want the dump_dir to be dependent on the 'sample_name' and 'scan' or\n", + "# other things.\n", "metadata[\"dump_dir\"] = (\n", " os.getcwd() + f'/results/{metadata[\"sample_name\"]}/S{metadata[\"scan\"]}/'\n", ")\n", @@ -106,7 +106,7 @@ "usetex = True # might not work if running the notebook directly on nice or slurm\n", "show = True\n", "verbose = True\n", - "debug = True\n", + "debug = False\n", "\n", "# PyNX parameters\n", "\n", @@ -326,7 +326,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.13" + "version": "3.10.0" }, "vscode": { "interpreter": { From fb1f07cbf18777b50a778a4b3587baccdb3f2445 Mon Sep 17 00:00:00 2001 From: Clatlan Date: Fri, 23 Feb 2024 16:23:28 +0100 Subject: [PATCH 2/3] implement the 3d surface projections in the pipeline --- cdiutils/plot/formatting.py | 69 ++++++-- cdiutils/plot/volume.py | 226 +++++++++++++++++++++++- cdiutils/process/processor.py | 317 ++++++++++++++++++---------------- 3 files changed, 444 insertions(+), 168 deletions(-) diff --git a/cdiutils/plot/formatting.py b/cdiutils/plot/formatting.py index 7798b882..35b435dc 100755 --- a/cdiutils/plot/formatting.py +++ b/cdiutils/plot/formatting.py @@ -1,9 +1,52 @@ -from typing import Union - -import colorcet import matplotlib import matplotlib.ticker as mticker import numpy as np +import colorcet + + +CXI_VIEW_PARAMETERS = { + "z+": {"axis": 0, "plane_axes": [2, 1], "yaxis_points_left": True}, + "z-": {"axis": 0, "plane_axes": [2, 1], "yaxis_points_left": False}, + "y+": {"axis": 1, "plane_axes": [2, 0], "yaxis_points_left": False}, + "y-": {"axis": 1, "plane_axes": [2, 0], "yaxis_points_left": True}, + "x+": {"axis": 2, "plane_axes": [0, 1], "yaxis_points_left": False}, + "x-": {"axis": 2, "plane_axes": [0, 1], "yaxis_points_left": True}, +} + + +def get_extents( + shape: tuple, + voxel_size: tuple | list | np.ndarray, + plane: list, + zero_centered: bool = True, +) -> tuple: + """Find the extents for matshow/imshow plotting, for a given plane. + + Args: + shape (tuple): the shape of the data to plot. + voxel_size (tuple | list | np.ndarray): the voxel size of + the data to plot. + voxel_size (tuple | list | np.ndarray): the voxel size of + the data to plot. + plane (list): what plane to get the extents from. Should be a + list of 2 axis integers. + zero_centered (bool, optional): whether the plot must be + centered at zero. Defaults to True. + + Returns: + tuple: first two values correspond to x-axis extent, last two + to the y-axis extent in the matshow/imshow plot. + """ + absolute_extents = [ + voxel_size[i] * shape[i] // (2 if zero_centered else 1) + for i in range(3) + ] + return ( + -absolute_extents[plane[0]] if zero_centered else 0, + absolute_extents[plane[0]], + -absolute_extents[plane[1]] if zero_centered else 0, + absolute_extents[plane[1]], + ) def set_plot_configs(): @@ -84,12 +127,12 @@ def set_plot_configs(): def update_plot_params( style: str = "default", usetex: bool = True, - use_siunitx: bool = False, + use_siunitx: bool = True, **kwargs ) -> None: """Update the matplotlib plot parameters to plublication style""" - if style in ("default", "nature", "NATURE"): + if style in ("default", "nature"): parameters = { "lines.linewidth": 1, "lines.markersize": 1, @@ -118,7 +161,7 @@ def update_plot_params( **parameters ) if use_siunitx: - usetex = True + # bypass usetex value, usetex will be set to True matplotlib.pyplot.rcParams.update( **{ 'text.latex.preamble': ( @@ -128,17 +171,19 @@ def update_plot_params( + ( r'\usepackage{sansmath} \sansmath' r'\usepackage{textgreek}' - if style == "nature" else r'\usepackage{amsmath}' + if style in ("default", "nature") + else r'\usepackage{amsmath}' ) - ) + ), + "text.usetex": True } ) - if usetex: + if usetex and not use_siunitx: matplotlib.pyplot.rcParams.update( **{ "mathtext.default": "regular", - "text.usetex": usetex, + "text.usetex": True, "font.family": "sans-serif", "font.sans-serif": ["Liberation Sans"] } @@ -162,7 +207,7 @@ def get_figure_size( """ Get the figure dimensions to avoid scaling in LaTex. - This function was copied from + This function was taken from https://jwalton.info/Embed-Publication-Matplotlib-Latex/ :param width: Document width in points, or string of predefined @@ -174,7 +219,7 @@ def get_figure_size( :return: dimensions of the figure in inches (tuple) """ if width == 'default': - width_pt = 390 + width_pt = 420 elif width == 'thesis': width_pt = 455.30101 elif width == 'beamer': diff --git a/cdiutils/plot/volume.py b/cdiutils/plot/volume.py index 9a9d3880..ccd4a08e 100755 --- a/cdiutils/plot/volume.py +++ b/cdiutils/plot/volume.py @@ -1,10 +1,228 @@ import matplotlib.pyplot as plt import matplotlib as mpl +from mpl_toolkits.axes_grid1 import make_axes_locatable import numpy as np -from mpl_toolkits.mplot3d import Axes3D +import warnings +from cdiutils.plot.formatting import ( + get_figure_size, + get_extents, + CXI_VIEW_PARAMETERS +) +from cdiutils.utils import ( + find_suitable_array_shape, + CroppingHandler, + nan_to_zero +) -def plot_3D_object( + +def hemisphere_projection( + data: np.ndarray, + support: np.ndarray, + axis: int, + looking_from_dowstream: bool = True +) -> np.ndarray: + """Compute the hemisphere projection of a volume along one axis. + + Args: + data (np.ndarray): the volume data to project. + support (np.ndarray): the support of the reconstructed data. + axis (int): the axis along which to project. + looking_from_dowstream (bool, optional): The direction along + axis, positive-going (True) or negative-going (False). + Defaults to True. + + Returns: + np.ndarray: the 2D array corresponding to the projection. + """ + # Make sure we have 0 values instead of nan + support = nan_to_zero(support) + + # Find the support surface + if looking_from_dowstream: + support_surface = np.cumsum(support, axis=axis) + else: + slices = tuple( + [np.s_[:]] * axis + [np.s_[::-1]] + [np.s_[:]] * (2 - axis) + ) + support_surface = np.cumsum(support[slices], axis=axis)[slices] + + support_surface = np.where(support_surface > 1, 0, support_surface) + half_shell_strain = np.where(support_surface == 0, np.nan, data) + + # Some warning is expecting here as mean of empty slices may occur + with warnings.catch_warnings(): + warnings.simplefilter("ignore", category=RuntimeWarning) + # project the half shell strain along the direction provided + # by axis + return np.nanmean(half_shell_strain, axis=axis) + + +def plot_3d_surface_projections( + data: np.ndarray, + support: np.ndarray, + voxel_size: tuple | list | np.ndarray, + view_parameters: dict = None, + figsize: tuple = None, + title: str = None, + cbar_title: str = None, + **figure_parameters +) -> mpl.figure.Figure: + """Plot 3D projected views from a 3D object. + + Args: + data (np.ndarray): the data to plot. + support (np.ndarray): the support of the reconstructed object. + voxel_size (tuple | list | np.ndarray): the voxel size of + the data to plot. + view_parameters (dict, optional): some parameters required for + setting the plot views. Defaults to CXI_VIEW_PARAMETERS. + figsize (tuple, optional): the size of the figure. Defaults to + None. + title (str, optional): the title of the figure. Defaults to + None. + cbar_title (str, optional): the title of the colour bar. + Defaults to None. + + Returns: + matplotlib.figure.Figure: the figure. + """ + if view_parameters is None: + view_parameters = CXI_VIEW_PARAMETERS + + if figsize is None: + figsize = get_figure_size(subplots=(3, 3)) + + cbar_size, cbar_pad = 0.07, 0.4 + figure, axes = plt.subplots( + 2, 3, + figsize=figsize, + gridspec_kw={'height_ratios': [1/(1-(cbar_pad+cbar_size)), 1]} + ) + shape = find_suitable_array_shape(support, symmetrical_shape=False) + cropped_support, _, _, roi = CroppingHandler.chain_centering( + support, + output_shape=shape, + methods=["com"] + ) + + cropped_data = data[CroppingHandler.roi_list_to_slices(roi)] + + for v in view_parameters.keys(): + looking_from_dowstream = False + row = 0 + if v.endswith("+"): + looking_from_dowstream = True + row = 1 + + ax = axes[row, view_parameters[v]["axis"]] + + projection = hemisphere_projection( + cropped_data, + cropped_support, + axis=view_parameters[v]["axis"], + looking_from_dowstream=looking_from_dowstream + ) + + # Swap axes for matshow if the first plane axis is less than the + # second, ensuring correct orientation where the first plane + # corresponds to the y-axis and the seconde plane to the x-axis. + # If first plane axis > second plane axis, the default orientation is + # correct, and no swapping is needed. + if view_parameters[v]["plane_axes"] == sorted( + view_parameters[v]["plane_axes"] + ): + projection = np.swapaxes(projection, axis1=0, axis2=1) + + # to handle extent and origin please refer to + # https://matplotlib.org/stable/users/explain/artists/imshow_extent.html#imshow-extent + extent = get_extents( + shape, + voxel_size, + view_parameters[v]["plane_axes"] + ) + + if view_parameters[v]["yaxis_points_left"]: + # flip the horizontal extent, and the image horizontally + extent = (extent[1], extent[0], *extent[2:]) + projection = projection[np.s_[:, ::-1]] + + image = ax.matshow( + projection, + extent=extent, + origin="lower", + interpolation="antialiased", + **figure_parameters + ) + ax.set_title(v, y=0.95) + + # Set a new boolean for whether y-axis should be right or left + yaxis_left = view_parameters[v]["yaxis_points_left"] + + # Remove the useless spines + ax.spines[ + ["top", "left" if yaxis_left else "right"]].set_visible(False) + + # Set the position of the spines + ax.spines["right" if yaxis_left else "left"].set_position( + ("axes", yaxis_left) + ) + + # Customize ticks and tick labels + ax.xaxis.set_ticks_position("bottom") + ax.yaxis.set_ticks_position("right" if yaxis_left else "left") + ax.yaxis.set_label_position("right" if yaxis_left else "left") + + # Plot the shaft of the axis + ax.plot( + yaxis_left, + 1, + "^k", + transform=ax.transAxes, + clip_on=False + ) + ax.plot( + 1-yaxis_left, 0, + "k", + transform=ax.transAxes, + clip_on=False + ) + xlabel = ( + r"$z$\textsubscript{CXI}" + if view_parameters[v]["plane_axes"][0] == 0 + else r"$y$\textsubscript{CXI}" + if view_parameters[v]["plane_axes"][0] == 1 + else r"$x$\textsubscript{CXI}" + ) + ylabel = ( + r"$z$\textsubscript{CXI}" + if view_parameters[v]["plane_axes"][1] == 0 + else r"$y$\textsubscript{CXI}" + if view_parameters[v]["plane_axes"][1] == 1 + else r"$x$\textsubscript{CXI}" + ) + ax.set_xlabel(xlabel + "(nm)", labelpad=1) + ax.set_ylabel(ylabel + "(nm)", labelpad=1) + ax.tick_params(axis='both', which='major', pad=1.5) + + ax.locator_params(nbins=5) + + divider = make_axes_locatable(axes[0, 1]) + cax = divider.append_axes("top", size=cbar_size, pad=cbar_pad) + figure.colorbar( + image, + cax=cax, + extend="both", + orientation="horizontal", + ) + cax.set_title(cbar_title) + + figure.suptitle(title) + figure.tight_layout() + return figure + + +def plot_3d_object( data, support=None, cmap="turbo", @@ -14,7 +232,7 @@ def plot_3D_object( show=True, marker="H", alpha=1 - ): +): """ Plot a 3D object. @@ -72,7 +290,7 @@ def plot_3D_object( return fig -def plot_3D_vector_field( +def plot_3d_vector_field( data, support, arrow=True, diff --git a/cdiutils/process/processor.py b/cdiutils/process/processor.py index 057e54aa..663e5378 100644 --- a/cdiutils/process/processor.py +++ b/cdiutils/process/processor.py @@ -34,6 +34,7 @@ plot_final_object_fft ) from cdiutils.plot.colormap import RED_TO_TEAL +from cdiutils.plot.volume import plot_3d_surface_projections def loader_factory(metadata: dict) -> BlissLoader | SpecLoader | SIXS2022Loader: @@ -123,11 +124,11 @@ def __init__( }, "strain": { "name": "different_strain_methods", - "debug": True + "debug": False }, "displacement_gradient": { "name": "displacement_gradient", - "debug": True + "debug": False }, "amplitude": { "name": "amplitude_distribution_plot", @@ -139,11 +140,15 @@ def __init__( }, "q_lab_orthogonalization": { "name": "q_lab_orthogonalization_plot", - "debug": True + "debug": False }, "final_object_fft": { "name": "final_object_fft", - "debug": True + "debug": False + }, + "3d_strain": { + "name": "3d_strain", + "debug": False } } for value in self.figures.values(): @@ -972,169 +977,177 @@ def postprocess(self) -> None: "Something went wrong during plotting. " "Won't plot summary slice plot." ) from exc + self.figures["3d_strain"]["figure"] = plot_3d_surface_projections( + data=self.structural_properties["het_strain"], + support=self.structural_properties["support"], + voxel_size=self.voxel_size, + cmap="cet_CET_D13", + vmin=-np.nanmax(np.abs(self.structural_properties["het_strain"])), + vmax=np.nanmax(np.abs(self.structural_properties["het_strain"])), + cbar_title="Strain (\%)", + title="3D view in the CXI frame" + ) - if self.params["debug"]: - strain_plots = { - k: self.structural_properties[k] - for k in [ - "het_strain", "het_strain_from_dspacing", - "het_strain_from_dspacing", "numpy_het_strain", - "het_strain_with_ramp" - ] - } - self.figures["strain"]["figure"] = summary_slice_plot( - title=f"Strain check figure, {self.sample_name}, {self.scan}", + strain_plots = { + k: self.structural_properties[k] + for k in [ + "het_strain", "het_strain_from_dspacing", + "het_strain_from_dspacing", "numpy_het_strain", + "het_strain_with_ramp" + ] + } + self.figures["strain"]["figure"] = summary_slice_plot( + title=f"Strain check figure, {self.sample_name}, {self.scan}", + support=zero_to_nan(self.structural_properties["support"]), + dpi=200, + voxel_size=self.voxel_size, + isosurface=self.params["isosurface"], + det_reference_voxel=self.params["det_reference_voxel"], + averaged_dspacing=self.averaged_dspacing, + averaged_lattice_parameter=self.averaged_lattice_parameter, + single_vmin=-self.structural_properties["het_strain"].ptp()/2, + single_vmax=self.structural_properties["het_strain"].ptp()/2, + **strain_plots + ) + + # take care of the axis names for the displacement gradient + # plots + axis_names = [ + r"z_{cxi}", r"y_{cxi}", r"x_{cxi}" + ] + if self.params["usetex"]: + axis_title_template = ( + r"$\frac{\partial u_" + "{" + + f"{''.join([str(e) for e in self.params['hkl']])}" + + "}}" + ) + titles = [ + axis_title_template + r"{\partial " + axis_names[i] + "}$" + for i in range(3) + ] + else: + axis_title_template = ( + "du_" + "{" + + f"{''.join([str(e) for e in self.params['hkl']])}" + "}" + ) + titles = [ + fr"${axis_title_template}/d{axis_names[i]}$" + for i in range(3) + ] + + displacement_gradient_plots = { + titles[i]: ( + self.structural_properties["displacement_gradient"][i] + ) + for i in range(3) + } + ptp_value = ( + np.nanmax( + self.structural_properties["displacement_gradient"][0]) + - np.nanmin( + self.structural_properties["displacement_gradient"][0]) + ) + self.figures["displacement_gradient"]["figure"] = ( + summary_slice_plot( + title=( + "Displacement gradient, " + f"{self.sample_name}, {self.scan}" + ), support=zero_to_nan(self.structural_properties["support"]), dpi=200, voxel_size=self.voxel_size, isosurface=self.params["isosurface"], det_reference_voxel=self.params["det_reference_voxel"], averaged_dspacing=self.averaged_dspacing, - averaged_lattice_parameter=self.averaged_lattice_parameter, - single_vmin=-self.structural_properties["het_strain"].ptp()/2, - single_vmax=self.structural_properties["het_strain"].ptp()/2, - **strain_plots + averaged_lattice_parameter=( + self.averaged_lattice_parameter + ), + single_vmin=-ptp_value/2, + single_vmax=ptp_value/2, + cmap=RED_TO_TEAL, + **displacement_gradient_plots ) + ) - # take care of the axis names for the displacement gradient - # plots - axis_names = [ - r"z_{cxi}", r"y_{cxi}", r"x_{cxi}" + # load the orthogonalized intensity computed during + # preprocessing + file_path = ( + f"{self.dump_dir}cdiutils_S{self.scan}" + "_orthogonalized_intensity.npz" + ) + with np.load(file_path) as npzfile: + orthogonalized_intensity = npzfile["orthogonalized_intensity"] + exp_data_q_lab_grid = [ + npzfile["q_xlab"], + npzfile["q_ylab"], + npzfile["q_zlab"] ] - if self.params["usetex"]: - axis_title_template = ( - r"$\frac{\partial u_" + "{" - + f"{''.join([str(e) for e in self.params['hkl']])}" - + "}}" - ) - titles = [ - axis_title_template + r"{\partial " + axis_names[i] + "}$" - for i in range(3) - ] - else: - axis_title_template = ( - "du_" + "{" - + f"{''.join([str(e) for e in self.params['hkl']])}" + "}" - ) - titles = [ - fr"${axis_title_template}/d{axis_names[i]}$" - for i in range(3) - ] - - displacement_gradient_plots = { - titles[i]: ( - self.structural_properties["displacement_gradient"][i] - ) - for i in range(3) - } - ptp_value = ( - np.nanmax( - self.structural_properties["displacement_gradient"][0]) - - np.nanmin( - self.structural_properties["displacement_gradient"][0]) - ) - self.figures["displacement_gradient"]["figure"] = ( - summary_slice_plot( - title=( - "Displacement gradient, " - f"{self.sample_name}, {self.scan}" - ), - support=zero_to_nan(self.structural_properties["support"]), - dpi=200, - voxel_size=self.voxel_size, - isosurface=self.params["isosurface"], - det_reference_voxel=self.params["det_reference_voxel"], - averaged_dspacing=self.averaged_dspacing, - averaged_lattice_parameter=( - self.averaged_lattice_parameter - ), - single_vmin=-ptp_value/2, - single_vmax=ptp_value/2, - cmap=RED_TO_TEAL, - **displacement_gradient_plots - ) - ) - - if self.params["debug"]: - # load the orthogonalized intensity computed during - # preprocessing - file_path = ( - f"{self.dump_dir}cdiutils_S{self.scan}" - "_orthogonalized_intensity.npz" - ) - with np.load(file_path) as npzfile: - orthogonalized_intensity = npzfile["orthogonalized_intensity"] - exp_data_q_lab_grid = [ - npzfile["q_xlab"], - npzfile["q_ylab"], - npzfile["q_zlab"] - ] - shape = orthogonalized_intensity.shape - # convert to lab conventions and pad the data - # We must multiply by -1 the phase to compare with the - # measured intensity. - final_object_fft = symmetric_pad( - self.space_converter.cxi_to_lab_conventions( - self.structural_properties["amplitude"] - * np.exp(-1j*self.structural_properties["phase"]) - ), - final_shape=shape - ) - final_object_fft = np.abs(np.fft.ifftshift( - np.fft.fftn( - np.fft.fftshift(final_object_fft) - ) - )) ** 2 - - extension = np.multiply(self.voxel_size, shape) - voxel_size_of_fft_object = 2 * np.pi / (10 * extension) - - final_object_q_lab_grid = ( - np.arange( - -shape[0]//2, shape[0]//2, 1 - ) * voxel_size_of_fft_object[0], - np.arange( - -shape[1]//2, shape[1]//2, 1 - ) * voxel_size_of_fft_object[1], - np.arange( - -shape[2]//2, shape[2]//2, 1 - ) * voxel_size_of_fft_object[2], - ) - final_object_q_lab_grid = tuple( - shift + grid - for shift, grid in zip( - self.space_converter.q_space_shift, - final_object_q_lab_grid - ) + shape = orthogonalized_intensity.shape + # convert to lab conventions and pad the data + # We must multiply by -1 the phase to compare with the + # measured intensity. + final_object_fft = symmetric_pad( + self.space_converter.cxi_to_lab_conventions( + self.structural_properties["amplitude"] + * np.exp(-1j*self.structural_properties["phase"]) + ), + final_shape=shape + ) + final_object_fft = np.abs(np.fft.ifftshift( + np.fft.fftn( + np.fft.fftshift(final_object_fft) + ) + )) ** 2 + + extension = np.multiply(self.voxel_size, shape) + voxel_size_of_fft_object = 2 * np.pi / (10 * extension) + + final_object_q_lab_grid = ( + np.arange( + -shape[0]//2, shape[0]//2, 1 + ) * voxel_size_of_fft_object[0], + np.arange( + -shape[1]//2, shape[1]//2, 1 + ) * voxel_size_of_fft_object[1], + np.arange( + -shape[2]//2, shape[2]//2, 1 + ) * voxel_size_of_fft_object[2], + ) + final_object_q_lab_grid = tuple( + shift + grid + for shift, grid in zip( + self.space_converter.q_space_shift, + final_object_q_lab_grid ) + ) - # find the position in the cropped detector frame - roi = CroppingHandler.get_roi( - self.params["preprocessing_output_shape"], - self.params["det_reference_voxel"] - ) - cropped_det_ref = tuple( - p - r if r else p # if r is None, p-r must be p - for p, r in zip( - self.params["det_reference_voxel"], roi[::2]) - ) - where_in_ortho_space = ( - self.space_converter.index_det_to_index_of_q_lab( - cropped_det_ref - ) + # find the position in the cropped detector frame + roi = CroppingHandler.get_roi( + self.params["preprocessing_output_shape"], + self.params["det_reference_voxel"] + ) + cropped_det_ref = tuple( + p - r if r else p # if r is None, p-r must be p + for p, r in zip( + self.params["det_reference_voxel"], roi[::2]) + ) + where_in_ortho_space = ( + self.space_converter.index_det_to_index_of_q_lab( + cropped_det_ref ) + ) - self.figures["final_object_fft"]["figure"] = plot_final_object_fft( - final_object_fft, - orthogonalized_intensity, - final_object_q_lab_grid, - exp_data_q_lab_grid, - where_in_ortho_space=where_in_ortho_space, - title=( - r"FFT of final object \textit{vs.} experimental data" - f", {self.sample_name}, {self.scan}" - ) + self.figures["final_object_fft"]["figure"] = plot_final_object_fft( + final_object_fft, + orthogonalized_intensity, + final_object_q_lab_grid, + exp_data_q_lab_grid, + where_in_ortho_space=where_in_ortho_space, + title=( + r"FFT of final object \textit{vs.} experimental data" + f", {self.sample_name}, {self.scan}" ) + ) def save_postprocessed_data(self) -> None: """ From 0a293ceb20fcc15d5fb1f4a8a240d089cf3fb993 Mon Sep 17 00:00:00 2001 From: Clatlan Date: Fri, 23 Feb 2024 16:34:48 +0100 Subject: [PATCH 3/3] edit figure titles --- cdiutils/process/processor.py | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/cdiutils/process/processor.py b/cdiutils/process/processor.py index 663e5378..43530940 100644 --- a/cdiutils/process/processor.py +++ b/cdiutils/process/processor.py @@ -962,7 +962,7 @@ def postprocess(self) -> None: } try: self.figures["postprocessing"]["figure"] = summary_slice_plot( - title=f"Summary figure, {self.sample_name}, {self.scan}", + title=f"Summary figure, {self.sample_name}, S{self.scan}", support=zero_to_nan(self.structural_properties["support"]), dpi=200, voxel_size=self.voxel_size, @@ -985,7 +985,7 @@ def postprocess(self) -> None: vmin=-np.nanmax(np.abs(self.structural_properties["het_strain"])), vmax=np.nanmax(np.abs(self.structural_properties["het_strain"])), cbar_title="Strain (\%)", - title="3D view in the CXI frame" + title=f"3D views of the strain, {self.sample_name}, S{self.scan}" ) strain_plots = { @@ -997,7 +997,7 @@ def postprocess(self) -> None: ] } self.figures["strain"]["figure"] = summary_slice_plot( - title=f"Strain check figure, {self.sample_name}, {self.scan}", + title=f"Strain check figure, {self.sample_name}, S{self.scan}", support=zero_to_nan(self.structural_properties["support"]), dpi=200, voxel_size=self.voxel_size, @@ -1051,7 +1051,7 @@ def postprocess(self) -> None: summary_slice_plot( title=( "Displacement gradient, " - f"{self.sample_name}, {self.scan}" + f"{self.sample_name}, S{self.scan}" ), support=zero_to_nan(self.structural_properties["support"]), dpi=200, @@ -1145,7 +1145,7 @@ def postprocess(self) -> None: where_in_ortho_space=where_in_ortho_space, title=( r"FFT of final object \textit{vs.} experimental data" - f", {self.sample_name}, {self.scan}" + f", {self.sample_name}, S{self.scan}" ) )