From 3979d330f4db1d43d2ae99e46177a89bfe8ccddb Mon Sep 17 00:00:00 2001 From: Patrick Curran Date: Wed, 13 Nov 2024 15:38:15 +0000 Subject: [PATCH] Initial commit --- defdap/base.py | 1 + defdap/inspector_optical.py | 333 ++++++++++++++ defdap/optical.py | 82 +++- defdap/plotting.py | 3 +- notebooks/example_notebook.ipynb | 6 +- notebooks/optical.ipynb | 741 +++++++++++++++++++++++++++++-- 6 files changed, 1114 insertions(+), 52 deletions(-) create mode 100644 defdap/inspector_optical.py diff --git a/defdap/base.py b/defdap/base.py index 5d54710..f2b6b12 100755 --- a/defdap/base.py +++ b/defdap/base.py @@ -921,6 +921,7 @@ def grain_data(self, map_data): return grain_data + def grain_map_data(self, map_data=None, grain_data=None, bg=np.nan): """Extract a single grain map from the given map data. diff --git a/defdap/inspector_optical.py b/defdap/inspector_optical.py new file mode 100644 index 0000000..0ad3308 --- /dev/null +++ b/defdap/inspector_optical.py @@ -0,0 +1,333 @@ +# Copyright 2023 Mechanics of Microstructures Group +# at The University of Manchester +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import numpy as np +from scipy.stats import linregress +from skimage.draw import line as skimage_line +import ast + +from defdap.plotting import Plot, GrainPlot +from defdap import hrdic + +from typing import List + + +class GrainInspector: + """ + Class containing the interactive grain inspector tool for slip trace analysis + and relative displacement ratio analysis. + + """ + + def __init__(self, + selected_dic_map: 'optical.Map'): + """ + + Parameters + ---------- + selected_dic_map + DIC map to run grain inspector on. + """ + + # Initialise some values + self.grain_id = 0 + self.selected_dic_map = selected_dic_map + self.selected_ebsd_map = self.selected_dic_map.ebsd_map + self.selected_dic_grain = self.selected_dic_map[self.grain_id] + self.selected_ebsd_grain = self.selected_dic_grain.ebsd_grain + + # Plot window + self.plot = Plot(ax=None, make_interactive=True, figsize=(13, 8), title='Grain Inspector') + div_frac = 0.7 + + # Remove key bindings for figure to suppress errors + self.plot.fig.canvas.mpl_disconnect(self.plot.fig.canvas.manager.key_press_handler_id) + + # Buttons + self.plot.add_button( + 'Save\nLine', self.save_line, (div_frac, 0.48, 0.05, 0.04)) + self.plot.add_button( + 'Previous\nGrain', lambda e, p: self.goto_grain(self.grain_id - 1, p), (div_frac, 0.94, 0.05, 0.04)) + self.plot.add_button( + 'Next\nGrain', lambda e, p: self.goto_grain(self.grain_id + 1, p), (div_frac + 0.06, 0.94, 0.05, 0.04)) + self.plot.add_button( + 'Run All STA', self.batch_run_sta, (0.85, 0.07, 0.11, 0.04)) + self.plot.add_button( + 'Clear\nAll Lines', self.clear_all_lines, (div_frac + 0.2, 0.48, 0.05, 0.04)) + + + # Text boxes + self.plot.add_text_box(label='Go to \ngrain ID:', loc=(div_frac + 0.17, 0.94, 0.05, 0.04), + submit_handler=self.goto_grain) + self.plot.add_text_box(label='Remove\nID:', loc=(div_frac + 0.1, 0.48, 0.05, 0.04), + submit_handler=self.remove_line) + + # Axes + self.max_shear_axis = self.plot.add_axes((0.05, 0.4, 0.65, 0.55)) + self.slip_trace_axis = self.plot.add_axes((0.25, 0.05, 0.5, 0.3)) + self.unit_cell_axis = self.plot.add_axes((0.05, 0.055, 0.2, 0.3), proj='3d') + self.grain_info_axis = self.plot.add_axes((div_frac, 0.86, 0.25, 0.06)) + self.line_info_axis = self.plot.add_axes((div_frac, 0.55, 0.25, 0.3)) + self.groups_info_axis = self.plot.add_axes((div_frac, 0.15, 0.25, 0.3)) + + self.grain_plot = self.selected_dic_grain.plot_grain_data(grain_data=self.selected_dic_grain.data.image, + fig=self.plot.fig, + ax=self.max_shear_axis) + + self.plot.ax.axis('off') + + # Draw the stuff that will need to be redrawn often in a separate function + self.redraw() + + def goto_grain(self, + event: int, + plot): + """ Go to a specified grain ID. + + Parameters + ---------- + event + Grain ID to go to. + + """ + # Go to grain ID specified in event + self.grain_id = int(event) + self.grain_plot.arrow = None + self.selected_dic_grain = self.selected_dic_map[self.grain_id] + self.selected_ebsd_grain = self.selected_dic_grain.ebsd_grain + self.redraw() + + def save_line(self, + event: np.ndarray, + plot): + """ Save the start point, end point and angle of drawn line into the grain. + + Parameters + ---------- + event + Start x, start y, end x, end y point of line passed from drawn line. + + """ + + # Get angle of lines + line_angle = 90 - np.rad2deg(np.arctan2(self.grain_plot.p2[1] - self.grain_plot.p1[1], + self.grain_plot.p2[0] - self.grain_plot.p1[0])) + if line_angle > 180: + line_angle -= 180 + elif line_angle < 0: + line_angle += 180 + + # Two decimal places + points = [float("{:.2f}".format(point)) for point in self.grain_plot.p1 + self.grain_plot.p2] + line_angle = float("{:.2f}".format(line_angle)) + + # Save drawn line to the DIC grain + self.selected_dic_grain.points_list.append([points, line_angle, -1]) + + # Group lines and redraw + self.group_lines() + self.redraw_line() + + def group_lines(self, + grain: 'defdap.hrdic.Grain' = None): + """ + Group the lines drawn in the current grain item using a mean shift algorithm, + save the average angle and then detect the active slip planes. + + groups_list is a list of line groups: [id, angle, [slip plane id], [angular deviation] + + Parameters + ---------- + grain + Grain for which to group the slip lines. + + """ + + if grain is None: + grain = self.selected_dic_grain + + if grain.points_list == []: + grain.groups_list = [] + else: + for i, line in enumerate(grain.points_list): + angle = line[1] + if i == 0: + line[2] = 0 # Make group 0 for first detected angle + grain.groups_list = [[0, angle, 0, 0, 0]] + next_group = 1 + else: # If there is more that one angle + if np.any(np.abs(np.array([x[1] for x in grain.groups_list]) - angle) < 10): + # If within +- 5 degrees of existing group, set that as the group + group = np.argmin(np.abs(np.array([x[1] for x in grain.groups_list]) - angle)) + grain.points_list[i][2] = group + new_average = float('{0:.2f}'.format( + np.average([x[1] for x in grain.points_list if x[2] == group]))) + grain.groups_list[group][1] = new_average + else: + # Make new group and set + grain.groups_list.append([next_group, angle, 0, 0, 0]) + line[2] = next_group + next_group += 1 + + # Detect active slip systems in each group + for group in grain.groups_list: + active_planes = [] + deviation = [] + experimental_angle = group[1] + for idx, theoretical_angle in enumerate(np.rad2deg(grain.ebsd_grain.slip_trace_angles)): + if theoretical_angle - 5 < experimental_angle < theoretical_angle + 5: + active_planes.append(idx) + deviation.append(float('{0:.2f}'.format(experimental_angle - theoretical_angle))) + group[2] = active_planes + group[3] = deviation + + def clear_all_lines(self, + event, + plot): + """ Clear all lines in a given grain. + + """ + + self.selected_dic_grain.points_list = [] + self.selected_dic_grain.groups_list = [] + self.redraw() + + def remove_line(self, + event: int, + plot): + """ Remove single line [runs after submitting a text box]. + + Parameters + ---------- + event + Line ID to remove. + + """ + # Remove single line + del self.selected_dic_grain.points_list[int(event)] + self.group_lines() + self.redraw() + + def redraw(self): + """Draw items which need to be redrawn when changing grain ID. + + """ + + # Plot max shear for grain + self.max_shear_axis.clear() + self.grain_plot = self.selected_dic_grain.plot_grain_data(grain_data=self.selected_dic_grain.data.image, + fig=self.plot.fig, + ax=self.max_shear_axis) + + + # Draw unit cell + self.unit_cell_axis.clear() + self.selected_ebsd_grain.plot_unit_cell(fig=self.plot.fig, ax=self.unit_cell_axis) + + # Write grain info text + self.grain_info_axis.clear() + self.grain_info_axis.axis('off') + grain_info_text = 'Grain ID: {0} / {1}\n'.format(self.grain_id, len(self.selected_dic_map.grains) - 1) + + self.plot.add_text(self.grain_info_axis, 0, 1, grain_info_text, va='top', ha='left', + fontsize=10, fontfamily='monospace') + + # Detect lines + self.plot.add_event_handler('button_press_event', lambda e, p: self.grain_plot.line_slice(e, p)) + self.plot.add_event_handler('button_release_event', lambda e, p: self.grain_plot.line_slice(e, p)) + + self.redraw_line() + + def redraw_line(self): + """ + Draw items which need to be redrawn when adding a line. + + """ + # Write lines text and draw lines + title_text = 'List of lines' + lines_text = 'ID x0 y0 x1 y1 Angle Group\n' \ + '-----------------------------------------\n' + if self.selected_dic_grain.points_list: + for idx, points in enumerate(self.selected_dic_grain.points_list): + lines_text += '{0:<3} {1:<5.0f} {2:<5.0f} {3:<5.0f} {4:<5.0f} {5:<7.1f} {6:<5}\n'.format( + idx, *points[0], points[1], points[2]) + self.grain_plot.add_arrow(start_end=points[0], clear_previous=False, persistent=True, label=idx) + + self.line_info_axis.clear() + self.line_info_axis.axis('off') + self.plot.add_text(self.line_info_axis, 0, 1, title_text, va='top', + fontsize=10, fontfamily='monospace', weight='bold') + self.plot.add_text(self.line_info_axis, 0, 0.9, lines_text, va='top', + fontsize=10, fontfamily='monospace') + + # Write groups info text + title_text = 'List of groups' + + groupsTxt = 'ID Av. Angle System Dev\n' \ + '--------------------------\n' + if self.selected_dic_grain.groups_list: + for idx, group in enumerate(self.selected_dic_grain.groups_list): + groupsTxt += '{0:<3} {1:<10.1f} {2:<7} {3:<12}\n'.format( + idx, + group[1], + ','.join([str(np.round(i, 1)) for i in group[2]]), + ','.join([str(np.round(i, 1)) for i in group[3]])) + + self.groups_info_axis.clear() + self.groups_info_axis.axis('off') + self.plot.add_text(self.groups_info_axis, 0, 1, title_text, va='top', fontsize=10, fontfamily='monospace', + weight='bold') + self.plot.add_text(self.groups_info_axis, 0, 0.9, groupsTxt, va='top', fontsize=10, fontfamily='monospace') + + # Draw slip traces + self.slip_trace_axis.clear() + self.slip_trace_axis.set_aspect('equal', 'box') + + + slipPlot = GrainPlot(fig=self.plot.fig, + calling_grain=self.selected_dic_map[self.grain_id].ebsd_grain, ax=self.slip_trace_axis) + traces = slipPlot.add_slip_traces(top_only=True) + + self.slip_trace_axis.axis('off') + + # Draw slip bands + bands = [elem[1] for elem in self.selected_dic_grain.groups_list] + if self.selected_dic_grain.groups_list != None: + slipPlot.add_slip_bands(top_only=True, angles=list(np.deg2rad(bands))) + + + def batch_run_sta(self, + event, + plot): + """ Run slip trace analysis on all grains which hve slip trace lines drawn. + + """ + + # Print header + print("Grain\tEul1\tEul2\tEul3\tMaxSF\tGroup\tAngle\tSystem\tDev") + + # Print information for each grain + for idx, grain in enumerate(self.selected_dic_map): + if grain.points_list != []: + for group in grain.groups_list: + maxSF = np.max([item for sublist in grain.ebsd_grain.average_schmid_factors for item in sublist]) + eulers = self.selected_ebsd_grain.ref_ori.euler_angles() * 180 / np.pi + text = '{0}\t{1:.1f}\t{2:.1f}\t{3:.1f}\t{4:.3f}\t'.format( + idx, eulers[0], eulers[1], eulers[2], maxSF) + text += '{0}\t{1:.1f}\t{2}\t{3}'.format( + group[0], group[1], group[2], np.round(group[3], 3)) + print(text) + + diff --git a/defdap/optical.py b/defdap/optical.py index 7293c13..4c933a6 100644 --- a/defdap/optical.py +++ b/defdap/optical.py @@ -21,7 +21,8 @@ from defdap import base from defdap.utils import Datastore, report_progress from defdap import defaults - +from defdap.plotting import MapPlot +from defdap import inspector_optical class Map(base.Map): ''' @@ -203,16 +204,33 @@ def crop(self, map_data, binning=None): return map_data[..., min_y:max_y, min_x:max_x] - # def plot_optical_image(self, **kwargs): - # """Uses the Plot class to display the optical image.""" - # if self.optical is not None: - # # Pass the optical data into the create method of Plot - # plot_instance = MapPlot.create( - # calling_map=self, # Pass the current instance of Map - # map_data=self.optical, # Pass the loaded optical data - # **kwargs # Additional parameters can be passed here - # ) - # return plot_instance + def plot_optical_map(self, **kwargs): + """ + Plot a map with points coloured in IPF colouring, + with respect to a given sample direction. + + Parameters + ---------- + kwargs + Other arguments passed to :func:`defdap.plotting.MapPlot.create`. + + Returns + ------- + defdap.plotting.MapPlot + """ + # Ensure map_data is available in the optical object + if not hasattr(self, 'map_data'): + raise ValueError("The optical object must have 'map_data' attribute set.") + + # Set default plot parameters then update with any input + plot_params = { + 'calling_map': self, # Assuming `self` is the calling map (instance of base.Map) + 'map_data': self.optical # Access the map_data from the optical object + } + + plot_params.update(kwargs) + + return MapPlot.create(**plot_params) def link_ebsd_map(self, ebsd_map, transform_type="affine", **kwargs): """Calculates the transformation required to align EBSD dataset to DIC. @@ -356,6 +374,12 @@ def find_grains(self, algorithm=None, min_grain_size=10): self._grains = grain_list return grains + def grain_inspector(self): + """Run the grain inspector interactive tool. + + """ + inspector_optical.GrainInspector(selected_dic_map=self) + class Grain(base.Grain): """ @@ -364,7 +388,7 @@ class Grain(base.Grain): Attributes ---------- - dicMap : defdap.hrdic.Map + dicMap : defdap.optical.Map DIC map this grain is a member of ownerMap : defdap.hrdic.Map DIC map this grain is a member of @@ -398,11 +422,45 @@ def __init__(self, grain_id, optical_map, group_id): self.ebsd_grain = None self.ebsd_map = None + self.points_list = [] # Lines drawn for STA + self.groups_list = [] # Unique angles drawn for STA + # self.plot_default = lambda *args, **kwargs: self.plot_max_shear( # plot_colour_bar=True, plot_scale_bar=True, plot_slip_traces=True, # plot_slip_bands=True, *args, **kwargs # ) + + @property + def ref_ori(self): + """Returns average grain orientation. + + Returns + ------- + defdap.quat.Quat + + """ + return self.ebsd_grain.ref_ori + + @property + def slip_traces(self): + """Returns list of slip trace angles based on EBSD grain orientation. + Returns + ------- + list + + """ + return self.ebsd_grain.slip_traces + + def calc_slip_traces(self, slip_systems=None): + """Calculates list of slip trace angles based on EBSD grain orientation. + + Parameters + ------- + slip_systems : defdap.crystal.SlipSystem, optional + + """ + self.ebsd_grain.calc_slip_traces(slip_systems=slip_systems) class BoundarySet(object): def __init__(self, dic_map, points, lines): diff --git a/defdap/plotting.py b/defdap/plotting.py index 8728f90..48f8945 100644 --- a/defdap/plotting.py +++ b/defdap/plotting.py @@ -851,7 +851,8 @@ def add_slip_traces(self, top_only=False, colours=None, pos=None, **kwargs): """ if colours is None: - colours = self.calling_grain.ebsd_grain.phase.slip_trace_colours + #colours = self.calling_grain.ebsd_grain.phase.slip_trace_colours #----------------------------------------- + colours = self.calling_grain.phase.slip_trace_colours slip_trace_angles = self.calling_grain.slip_traces self.add_traces(slip_trace_angles, colours, top_only, pos=pos, **kwargs) diff --git a/notebooks/example_notebook.ipynb b/notebooks/example_notebook.ipynb index 5c75499..134889d 100644 --- a/notebooks/example_notebook.ipynb +++ b/notebooks/example_notebook.ipynb @@ -1442,9 +1442,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Python [conda env:defdap]", + "display_name": "Python 3 (ipykernel)", "language": "python", - "name": "conda-env-defdap-py" + "name": "python3" }, "language_info": { "codemirror_mode": { @@ -1456,7 +1456,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.0" + "version": "3.12.6" } }, "nbformat": 4, diff --git a/notebooks/optical.ipynb b/notebooks/optical.ipynb index 193431a..8103f04 100644 --- a/notebooks/optical.ipynb +++ b/notebooks/optical.ipynb @@ -2,32 +2,54 @@ "cells": [ { "cell_type": "code", - "execution_count": null, + "execution_count": 1, "id": "a167425e-fc5d-4f0d-ad30-96ab225104a9", "metadata": {}, "outputs": [], "source": [ "from defdap import ebsd, optical\n", - "\n", + "import matplotlib.pyplot as plt\n", + "from defdap import plotting\n", + "import numpy as np\n", + "import defdap\n", "%matplotlib tk" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 2, "id": "a9b4bfb9", "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "text/plain": [ + "'C:\\\\Users\\\\mbgm5pc3\\\\DF_analysis\\\\DefDAP\\\\notebooks'" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ "pwd" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 3, "id": "6ae24a9b-6842-4540-b36b-3795c1d85f05", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(dimensions: 1170 x 935 pixels)\n" + ] + } + ], "source": [ "path = \"../tests/data/\"\n", "fname = \"F5-pol-Cy50.png\"\n", @@ -57,30 +79,48 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "de922a4a-44ff-45a9-8f1d-2fb99297b698", "metadata": {}, "outputs": [], "source": [ - "optical_map.plot_map('image', plot_scale_bar=True)" + "#optical_map.plot_map('image', plot_scale_bar=True)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 7, "id": "a0eaeccb-5e58-428e-b448-903ed9bb1942", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Loaded EBSD data (dimensions: 852 x 734 pixels, step size: 3.0 um)\n" + ] + } + ], "source": [ "ebsd_map = ebsd.Map(path + \"f-5-test-region\")" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 8, "id": "2a53dd86", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Finished building quaternion array (0:00:06) \n", + "Finished finding grain boundaries (0:00:15) \n", + "Finished finding grains (0:00:06) \n" + ] + } + ], "source": [ "ebsd_map.data.generate('grain_boundaries', misori_tol=10)\n", "ebsd_map.data.generate('grains', min_grain_size=200)" @@ -88,31 +128,40 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "id": "df57b528", "metadata": {}, "outputs": [], "source": [ - "ebsd_map.plot_map('orientation', 'IPF_x', plot_scale_bar=True)" + "#ebsd_map.plot_map('orientation', 'IPF_x', plot_scale_bar=True)" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "id": "bb9b6f7c-a22a-4ca5-83be-604b40ae67f8", "metadata": {}, "outputs": [], "source": [ - "ebsd_map.set_homog_point(map_name=\"orientation\", component='IPF_z')\n", - "optical_map.set_homog_point()" + "#ebsd_map.set_homog_point(map_name=\"orientation\", component='IPF_z')\n", + "#optical_map.set_homog_point()" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 11, "id": "e8032bfb-1f04-4ffc-9ad4-20a47b3bb9e9", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[]\n", + "[]\n" + ] + } + ], "source": [ "print(ebsd_map.frame.homog_points)\n", "print(optical_map.frame.homog_points)" @@ -120,7 +169,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 12, "id": "4083f81f", "metadata": {}, "outputs": [], @@ -139,7 +188,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 13, "id": "df2355a3", "metadata": {}, "outputs": [], @@ -149,30 +198,59 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 14, "id": "f3868080", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Finished finding grains (0:00:00) \n" + ] + } + ], "source": [ "optical_map.data.generate('grains', min_grain_size=10, algorithm='warp')" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 15, "id": "95a897d4", "metadata": {}, "outputs": [], "source": [ - "optical_map.plot_map('image', plot_scale_bar=True, plot_gbs='pixel')" + "#optical_map.plot_map('image', plot_scale_bar=True, plot_gbs='pixel')" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 16, "id": "45efae0c", "metadata": {}, - "outputs": [], + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Datastore\n", + " image\n", + " proxigram\n", + " grains\n", + " Derived data:\n", + " phase_boundaries\n", + " grain_boundaries\n", + " point\n", + "Datastore\n", + " point\n", + " Derived data:\n", + " image\n", + " proxigram\n", + " grains\n" + ] + } + ], "source": [ "# data available for map\n", "print(optical_map.data)\n", @@ -182,39 +260,630 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 17, "id": "a9a65d90", "metadata": {}, "outputs": [], "source": [ - "optical_map.locate_grain()" + "#optical_map.locate_grain()" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 18, "id": "b955afea", "metadata": {}, "outputs": [], "source": [ - "grain = optical_map[20]\n", - "grain.plot_grain_data(grain_data=grain.data.image)" + "grain = optical_map[0]\n", + "#grain.plot_grain_data(grain_data=grain.data.image)" ] }, { "cell_type": "code", - "execution_count": null, + "execution_count": 19, "id": "4a647a29", "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "[np.float64(2.999151474212655),\n", + " np.float64(1.0753148211635188),\n", + " np.float64(3.0851021670346883),\n", + " np.float64(1.8888338214513343),\n", + " np.float64(1.4904308647148845),\n", + " np.float64(2.1774646990300246),\n", + " np.float64(0.7713158756193379),\n", + " np.float64(1.4627577132738598),\n", + " np.float64(0.023630610288391658),\n", + " np.float64(3.0570717723901626)]" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "grain.slip_traces" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "62d67bd5-a5bd-4a7e-8948-b405ca58f6d7", + "metadata": {}, + "outputs": [], + "source": [ + "from defdap.plotting import Plot, GrainPlot, MapPlot\n", + "from matplotlib import gridspec" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "0ad2c033-9c5d-4f77-b870-dec10b645e10", + "metadata": {}, + "outputs": [], + "source": [ + "optical_map.grain_inspector()" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "c7ae2f85-50c2-4072-a9cd-37723f18cc24", + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "defdap.plotting.GrainPlot" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "GrainPlot" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "e5844b01-86c3-4830-a078-2976dfaeeaaf", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "Traceback (most recent call last):\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\Lib\\site-packages\\matplotlib\\cbook.py\", line 298, in process\n", + " func(*args, **kwargs)\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\Lib\\site-packages\\matplotlib\\widgets.py\", line 244, in \n", + " return self._observers.connect('clicked', lambda event: func(event))\n", + " ^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\plotting.py\", line 126, in \n", + " btn.on_clicked(lambda e: click_handler(e, self))\n", + " ^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 64, in \n", + " 'Next\\nGrain', lambda e, p: self.goto_grain(self.grain_id + 1, p), (div_frac + 0.06, 0.94, 0.05, 0.04))\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 110, in goto_grain\n", + " self.redraw()\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 237, in redraw\n", + " self.selected_ebsd_grain.plot_unit_cell(fig=self.plot.fig, ax=self.unit_cell_axis)\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\ebsd.py\", line 1365, in plot_unit_cell\n", + " plot = Quat.plot_unit_cell(self.ref_ori, fig=fig, ax=ax, plot=plot,\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\quat.py\", line 557, in plot_unit_cell\n", + " eulerAngles = self.euler_angles()\n", + " ^^^^^^^^^^^^^^^^^\n", + "AttributeError: 'NoneType' object has no attribute 'euler_angles'\n", + "Traceback (most recent call last):\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\Lib\\site-packages\\matplotlib\\cbook.py\", line 298, in process\n", + " func(*args, **kwargs)\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\Lib\\site-packages\\matplotlib\\widgets.py\", line 244, in \n", + " return self._observers.connect('clicked', lambda event: func(event))\n", + " ^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\plotting.py\", line 126, in \n", + " btn.on_clicked(lambda e: click_handler(e, self))\n", + " ^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 64, in \n", + " 'Next\\nGrain', lambda e, p: self.goto_grain(self.grain_id + 1, p), (div_frac + 0.06, 0.94, 0.05, 0.04))\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 110, in goto_grain\n", + " self.redraw()\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 237, in redraw\n", + " self.selected_ebsd_grain.plot_unit_cell(fig=self.plot.fig, ax=self.unit_cell_axis)\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\ebsd.py\", line 1365, in plot_unit_cell\n", + " plot = Quat.plot_unit_cell(self.ref_ori, fig=fig, ax=ax, plot=plot,\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\quat.py\", line 557, in plot_unit_cell\n", + " eulerAngles = self.euler_angles()\n", + " ^^^^^^^^^^^^^^^^^\n", + "AttributeError: 'NoneType' object has no attribute 'euler_angles'\n", + "Traceback (most recent call last):\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\Lib\\site-packages\\matplotlib\\cbook.py\", line 298, in process\n", + " func(*args, **kwargs)\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\Lib\\site-packages\\matplotlib\\widgets.py\", line 244, in \n", + " return self._observers.connect('clicked', lambda event: func(event))\n", + " ^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\plotting.py\", line 126, in \n", + " btn.on_clicked(lambda e: click_handler(e, self))\n", + " ^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 64, in \n", + " 'Next\\nGrain', lambda e, p: self.goto_grain(self.grain_id + 1, p), (div_frac + 0.06, 0.94, 0.05, 0.04))\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 110, in goto_grain\n", + " self.redraw()\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 237, in redraw\n", + " self.selected_ebsd_grain.plot_unit_cell(fig=self.plot.fig, ax=self.unit_cell_axis)\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\ebsd.py\", line 1365, in plot_unit_cell\n", + " plot = Quat.plot_unit_cell(self.ref_ori, fig=fig, ax=ax, plot=plot,\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\quat.py\", line 557, in plot_unit_cell\n", + " eulerAngles = self.euler_angles()\n", + " ^^^^^^^^^^^^^^^^^\n", + "AttributeError: 'NoneType' object has no attribute 'euler_angles'\n", + "Traceback (most recent call last):\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\Lib\\site-packages\\matplotlib\\cbook.py\", line 298, in process\n", + " func(*args, **kwargs)\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\Lib\\site-packages\\matplotlib\\widgets.py\", line 244, in \n", + " return self._observers.connect('clicked', lambda event: func(event))\n", + " ^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\plotting.py\", line 126, in \n", + " btn.on_clicked(lambda e: click_handler(e, self))\n", + " ^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 64, in \n", + " 'Next\\nGrain', lambda e, p: self.goto_grain(self.grain_id + 1, p), (div_frac + 0.06, 0.94, 0.05, 0.04))\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 110, in goto_grain\n", + " self.redraw()\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 237, in redraw\n", + " self.selected_ebsd_grain.plot_unit_cell(fig=self.plot.fig, ax=self.unit_cell_axis)\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\ebsd.py\", line 1365, in plot_unit_cell\n", + " plot = Quat.plot_unit_cell(self.ref_ori, fig=fig, ax=ax, plot=plot,\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\quat.py\", line 557, in plot_unit_cell\n", + " eulerAngles = self.euler_angles()\n", + " ^^^^^^^^^^^^^^^^^\n", + "AttributeError: 'NoneType' object has no attribute 'euler_angles'\n", + "Traceback (most recent call last):\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\Lib\\site-packages\\matplotlib\\cbook.py\", line 298, in process\n", + " func(*args, **kwargs)\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\Lib\\site-packages\\matplotlib\\widgets.py\", line 244, in \n", + " return self._observers.connect('clicked', lambda event: func(event))\n", + " ^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\plotting.py\", line 126, in \n", + " btn.on_clicked(lambda e: click_handler(e, self))\n", + " ^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 64, in \n", + " 'Next\\nGrain', lambda e, p: self.goto_grain(self.grain_id + 1, p), (div_frac + 0.06, 0.94, 0.05, 0.04))\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 110, in goto_grain\n", + " self.redraw()\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 237, in redraw\n", + " self.selected_ebsd_grain.plot_unit_cell(fig=self.plot.fig, ax=self.unit_cell_axis)\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\ebsd.py\", line 1365, in plot_unit_cell\n", + " plot = Quat.plot_unit_cell(self.ref_ori, fig=fig, ax=ax, plot=plot,\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\quat.py\", line 557, in plot_unit_cell\n", + " eulerAngles = self.euler_angles()\n", + " ^^^^^^^^^^^^^^^^^\n", + "AttributeError: 'NoneType' object has no attribute 'euler_angles'\n", + "Traceback (most recent call last):\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\Lib\\site-packages\\matplotlib\\cbook.py\", line 298, in process\n", + " func(*args, **kwargs)\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\Lib\\site-packages\\matplotlib\\widgets.py\", line 244, in \n", + " return self._observers.connect('clicked', lambda event: func(event))\n", + " ^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\plotting.py\", line 126, in \n", + " btn.on_clicked(lambda e: click_handler(e, self))\n", + " ^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 64, in \n", + " 'Next\\nGrain', lambda e, p: self.goto_grain(self.grain_id + 1, p), (div_frac + 0.06, 0.94, 0.05, 0.04))\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 110, in goto_grain\n", + " self.redraw()\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 237, in redraw\n", + " self.selected_ebsd_grain.plot_unit_cell(fig=self.plot.fig, ax=self.unit_cell_axis)\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\ebsd.py\", line 1365, in plot_unit_cell\n", + " plot = Quat.plot_unit_cell(self.ref_ori, fig=fig, ax=ax, plot=plot,\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\quat.py\", line 557, in plot_unit_cell\n", + " eulerAngles = self.euler_angles()\n", + " ^^^^^^^^^^^^^^^^^\n", + "AttributeError: 'NoneType' object has no attribute 'euler_angles'\n", + "Traceback (most recent call last):\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\Lib\\site-packages\\matplotlib\\cbook.py\", line 298, in process\n", + " func(*args, **kwargs)\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\Lib\\site-packages\\matplotlib\\widgets.py\", line 244, in \n", + " return self._observers.connect('clicked', lambda event: func(event))\n", + " ^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\plotting.py\", line 126, in \n", + " btn.on_clicked(lambda e: click_handler(e, self))\n", + " ^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 64, in \n", + " 'Next\\nGrain', lambda e, p: self.goto_grain(self.grain_id + 1, p), (div_frac + 0.06, 0.94, 0.05, 0.04))\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 110, in goto_grain\n", + " self.redraw()\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 237, in redraw\n", + " self.selected_ebsd_grain.plot_unit_cell(fig=self.plot.fig, ax=self.unit_cell_axis)\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\ebsd.py\", line 1365, in plot_unit_cell\n", + " plot = Quat.plot_unit_cell(self.ref_ori, fig=fig, ax=ax, plot=plot,\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\quat.py\", line 557, in plot_unit_cell\n", + " eulerAngles = self.euler_angles()\n", + " ^^^^^^^^^^^^^^^^^\n", + "AttributeError: 'NoneType' object has no attribute 'euler_angles'\n", + "Traceback (most recent call last):\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\Lib\\site-packages\\matplotlib\\cbook.py\", line 298, in process\n", + " func(*args, **kwargs)\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\Lib\\site-packages\\matplotlib\\widgets.py\", line 244, in \n", + " return self._observers.connect('clicked', lambda event: func(event))\n", + " ^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\plotting.py\", line 126, in \n", + " btn.on_clicked(lambda e: click_handler(e, self))\n", + " ^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 64, in \n", + " 'Next\\nGrain', lambda e, p: self.goto_grain(self.grain_id + 1, p), (div_frac + 0.06, 0.94, 0.05, 0.04))\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 110, in goto_grain\n", + " self.redraw()\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 237, in redraw\n", + " self.selected_ebsd_grain.plot_unit_cell(fig=self.plot.fig, ax=self.unit_cell_axis)\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\ebsd.py\", line 1365, in plot_unit_cell\n", + " plot = Quat.plot_unit_cell(self.ref_ori, fig=fig, ax=ax, plot=plot,\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\quat.py\", line 557, in plot_unit_cell\n", + " eulerAngles = self.euler_angles()\n", + " ^^^^^^^^^^^^^^^^^\n", + "AttributeError: 'NoneType' object has no attribute 'euler_angles'\n", + "Traceback (most recent call last):\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\Lib\\site-packages\\matplotlib\\cbook.py\", line 298, in process\n", + " func(*args, **kwargs)\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\Lib\\site-packages\\matplotlib\\widgets.py\", line 244, in \n", + " return self._observers.connect('clicked', lambda event: func(event))\n", + " ^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\plotting.py\", line 126, in \n", + " btn.on_clicked(lambda e: click_handler(e, self))\n", + " ^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 64, in \n", + " 'Next\\nGrain', lambda e, p: self.goto_grain(self.grain_id + 1, p), (div_frac + 0.06, 0.94, 0.05, 0.04))\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 110, in goto_grain\n", + " self.redraw()\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 237, in redraw\n", + " self.selected_ebsd_grain.plot_unit_cell(fig=self.plot.fig, ax=self.unit_cell_axis)\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\ebsd.py\", line 1365, in plot_unit_cell\n", + " plot = Quat.plot_unit_cell(self.ref_ori, fig=fig, ax=ax, plot=plot,\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\quat.py\", line 557, in plot_unit_cell\n", + " eulerAngles = self.euler_angles()\n", + " ^^^^^^^^^^^^^^^^^\n", + "AttributeError: 'NoneType' object has no attribute 'euler_angles'\n", + "Traceback (most recent call last):\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\Lib\\site-packages\\matplotlib\\cbook.py\", line 298, in process\n", + " func(*args, **kwargs)\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\Lib\\site-packages\\matplotlib\\widgets.py\", line 244, in \n", + " return self._observers.connect('clicked', lambda event: func(event))\n", + " ^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\plotting.py\", line 126, in \n", + " btn.on_clicked(lambda e: click_handler(e, self))\n", + " ^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 64, in \n", + " 'Next\\nGrain', lambda e, p: self.goto_grain(self.grain_id + 1, p), (div_frac + 0.06, 0.94, 0.05, 0.04))\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 110, in goto_grain\n", + " self.redraw()\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\inspector_optical.py\", line 237, in redraw\n", + " self.selected_ebsd_grain.plot_unit_cell(fig=self.plot.fig, ax=self.unit_cell_axis)\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\ebsd.py\", line 1365, in plot_unit_cell\n", + " plot = Quat.plot_unit_cell(self.ref_ori, fig=fig, ax=ax, plot=plot,\n", + " ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n", + " File \"C:\\Users\\mbgm5pc3\\DF_analysis\\DefDAP\\defdap\\quat.py\", line 557, in plot_unit_cell\n", + " eulerAngles = self.euler_angles()\n", + " ^^^^^^^^^^^^^^^^^\n", + "AttributeError: 'NoneType' object has no attribute 'euler_angles'\n" + ] + } + ], + "source": [ + "grain_0 = optical_map[0].ebsd_grain" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f24b563b-2936-401b-8d59-b5378cf1f466", + "metadata": {}, + "outputs": [], + "source": [ + "print(grain_0)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17cd1902-bde4-4728-a6ad-5c72f167dc79", + "metadata": {}, + "outputs": [], + "source": [ + "fig = plt.figure(figsize=(9, 4))\n", + "\n", + "gs = gridspec.GridSpec(1, 2)\n", + "ax0 = plt.subplot(gs[0])\n", + "ax1 = plt.subplot(gs[1])\n", + "slipPlot = GrainPlot(fig=fig, calling_grain=ebsd_map[3], ax=ax0)\n", + "slipPlot.add_slip_traces()\n", + "\n", + "grain = optical_map[0]\n", + "ax0.axis('off')\n", + "grain.plot_grain_data(grain_data=grain.data.image, fig=fig, ax=ax1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "3c1a461c-c4ef-4c23-ab1e-07692d11b4f1", + "metadata": {}, + "outputs": [], + "source": [ + "# List slip angles for all the planes, draw a line and get its angle (need to be done within a interaction window), find the best match between \n", + "# the angles of slip planes and the line we draw\n", + "def calc_angle_drawn():\n", + " \n", + "lineAngle = 90-np.rad2deg(np.arctan2(self.grainPlot.p2[1]-self.grainPlot.p1[1],\n", + " self.grainPlot.p2[0]-self.grainPlot.p1[0]))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2f10bf5c-c696-4295-9fb2-bf520cda68c3", + "metadata": {}, + "outputs": [], + "source": [ + "fig = plt.figure(figsize=(13, 4))\n", + "\n", + "gs = gridspec.GridSpec(1, 3)\n", + "ax0 = plt.subplot(gs[0])\n", + "ax1 = plt.subplot(gs[1])\n", + "ax2 = plt.subplot(gs[2])\n", + "\n", + "grain_information = grain.slip_traces\n", + "\n", + "for i,angle in enumerate(grain_information):\n", + " if i == 0:\n", + " Color = 'red'\n", + " slip_plane = 'Basal'\n", + " elif i in [1,2,3]:\n", + " Color = 'blue'\n", + " slip_plane = 'Primastic'\n", + " elif i >3:\n", + " Color = 'green'\n", + " slip_plane = 'Pyramidal'\n", + " ax0.text(0, 1 - 0.1 * i, '{0} | Angle: {1}'.format(slip_plane,np.round(angle*180/np.pi,1)), color=Color)\n", + "\n", + "ax1.imshow(grain.data.image)\n", + "\n", + "\n", + "ax0.axis('off')\n", + "ax1.axis('off')\n", + "ax2.axis('off')" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2b73c99d-973a-40bc-a76c-6d3b6aef2795", + "metadata": {}, + "outputs": [], + "source": [ + "#grain.plot_grain_data(grain_data=grain.data.image,ax=ax1)\n", + "np.shape(grain.data.image)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "0ce037c8-075c-47b2-8ac6-e1b33009459f", + "metadata": {}, + "outputs": [], + "source": [ + "type(optical_map[0].extreme_coords)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "983fd2f2-852f-4b5f-8111-dbde514fecfc", + "metadata": {}, + "outputs": [], + "source": [ + "optical_map[0].grain_map_data()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "35c6a930-016e-4f67-a42c-930871bf589e", + "metadata": {}, + "outputs": [], + "source": [ + "outline = optical_map[0].grain_outline(bg=0, fg=1)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "02299089-56f8-4f27-bfeb-8779e74a89d0", + "metadata": {}, + "outputs": [], + "source": [ + "plt.figure()\n", + "plt.imshow(outline)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "31c25023-146a-4989-a605-4d199a201ac4", + "metadata": {}, + "outputs": [], + "source": [ + "outline" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "d7cc4b5d-4f98-4d91-a081-536fb3d01ac9", + "metadata": {}, + "outputs": [], + "source": [ + "grain_data = optical_map[0].grain_data" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ca7272ff-6d67-476f-8d17-a479fc6bd929", + "metadata": {}, + "outputs": [], + "source": [ + "print(grain_data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "68bfb305-51d4-4787-809a-82f8237969a9", + "metadata": {}, + "outputs": [], + "source": [ + "optical_map[0].centre_coords()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "4c538534-ddca-4634-8b16-335dbadc7bb1", + "metadata": {}, + "outputs": [], + "source": [ + "np.shape(grain_data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c61fde56-5d78-4153-9a17-038d0a91e217", + "metadata": {}, + "outputs": [], + "source": [ + "plt.imshow(grain_data)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "c00c2c00-c5c5-4306-9e2d-d2cdb9ab832f", + "metadata": {}, + "outputs": [], + "source": [ + "ebsd_map[0].slip_traces" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "05bce901-c239-4aac-a4b9-60b70d6414e9", + "metadata": {}, + "outputs": [], + "source": [ + "Plot.add_grain_numbers(ebsd_map)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "1a3dda64-4162-4cb5-9a44-8cd657830b88", + "metadata": {}, + "outputs": [], + "source": [ + "ebsd_map.add_grain_numbers()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "68bcc855-9aa3-4887-ab1a-94c773a54504", + "metadata": {}, + "outputs": [], + "source": [ + "np.shape(grain)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "71b8f64c-85cf-46b8-bb3f-244fc53133bb", + "metadata": {}, + "outputs": [], + "source": [ + "grain" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "72a9a7c9-b8dd-4556-b24d-14269f524df5", + "metadata": {}, + "outputs": [], + "source": [ + "\n", + "\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8cdd7a0e-8a1b-468f-a03e-a895bc1ba990", + "metadata": {}, + "outputs": [], + "source": [ + "optical_map.grain_inspector()" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "8965788e-e950-4db6-a0fb-e29b4c4583e5", + "metadata": {}, + "outputs": [], + "source": [] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "608d59f3-b6fa-48d8-84e8-c3446694c1f2", + "metadata": {}, "outputs": [], "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python (py311)", + "display_name": "Python 3 (ipykernel)", "language": "python", - "name": "py311" + "name": "python3" }, "language_info": { "codemirror_mode": { @@ -226,7 +895,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.11.7" + "version": "3.12.6" } }, "nbformat": 4,