Source code for upxo.ggrowth.mcgs


import numpy as np
from upxo._sup import gops
from upxo._sup import dataTypeHandlers as dth
import upxo._sup.decorators as decorators

[docs] class grid(): """ Description ----------- This is a core UPXO > mcgs class. Dependencies ------------ Parent class for: mcgs class Slots ----- __ui: DICT: User input (ui) dict uigrid: CLASS: ui: gridding parameters uisim: CLASS: ui: simulation par uigsc: CLASS: ui: grain strucure characterisation par uiint: CLASS: ui: intervals uigsprop: CLASS: ui: grain str property calculation par uigeorep: CLASS: ui: geometric representations cacl par _mcsteps_: LIST: stores history of mcsteps __g__: DICT: base dict template for grains __gprop__: DICT: base dict template for grain properties __gb__: DICT: base dict template for grain boundaries __gbprop__: DICT: base dict template for grain boundary properties g: DICT: Grains @latest mcstep m: LIST: available temporal slices xgr: np.ndarray: ygr: np.ndarray: zgr: np.ndarray: NL_dict: dict: Specifies Non-Locality detasils px_length: Iterable: Side lengths of the pixel px_size:, Area or volume of the pixel S: np.ndarray: State matrix sa: State martix modified enable fast consideration of Wrapped Boundary Condition vis: Stores instant of awrtwork class AIA: np.ndarray: Appended Index Array (@dev) AIA0: np.ndarray: Appended Index Array (@dev) AIA1: np.ndarray: Appended Index Array (@dev) xind: np.ndarray: xindices (3D only) yind: np.ndarray: yindices (3D only) zind: np.ndarray: zindices (3D only) xinda: np.ndarray: appended xindices (3D only) yinda: np.ndarray: appended yindices (3D only) zinda: np.ndarray: appended zindices (3D only) NLM_nd: np.ndarray: Non-Locality matrix NLM: np.ndarray: Non-locality matrix EAPGLB: PRIMARY GLOBAL Euler angle Definition -- state wise. EASGLB: SSECONDARY GLOBAL Euler angle definition -- state wise. Different from EAPGLB in that adjustments that happen to EAPGLB are carried out here and not in the proimary list. This is NOT available at the grid level but at the grain structure level. ##################################################################### ##################################################################### """ characterization_ID = 0 characterization_settings = None __slots__ = ('uigrid', 'uisim', 'uigsc', 'uiint', 'study', 'uigsprop', 'uimesh', 'uigeomrepr', '_mcsteps_', 'uidata_all', 'index', 'ndimg_label_pck', '__ui', '__g__', '__gprop__', '__gb__', '__gbprop__', 'gs', 'xgr', 'ygr', 'zgr', 'NL_dict', 'px_length', 'px_size', 'S', 's', 'sa', 'AIA', 'AIA0', 'AIA1', 'xind', 'yind', 'zind', 'xinda', 'yinda', 'zinda', 'NLM_nd', 'NLM', 'EAPGLB', 'tex', 'tslices_with_prop', 'vis', 'vizstyles', 'display_messages', '__info_message_display_level__', 'dt_dict' ) def __init__(self, study='independent', input_dashboard='input_dashboard.xls', consider_NLM_b=False, consider_NLM_d=False, AR_teevrate=0, AR_GrainAxis="-45", display_messages=True): """Initialise the instance.""" self.study = study if study == 'independent': from upxo.interfaces.user_inputs.gather_user_inputs import load_uidata from upxo._sup.data_templates import dict_templates uidata_all = load_uidata(input_dashboard) self.uigrid = uidata_all['uigrid'] self.uisim = uidata_all['uisim'] self.uigsc = uidata_all['uigsc'] self.uiint = uidata_all['uiint'] self.uigsprop = uidata_all['uigsprop'] self.uigeomrepr = uidata_all['uigeorep'] self.uimesh = uidata_all['uimesh'] self.__ui = uidata_all self.uidata_all = uidata_all self.dt_dict = dict_templates() self.initiate(consider_NLM_b=consider_NLM_b, consider_NLM_d=consider_NLM_d, AR_teevrate=AR_teevrate, AR_GrainAxis=AR_GrainAxis, display_messages=display_messages) elif study in ('para_sweep'): # Parameters to be manually set pass elif study == 'restart': pass def __iter__(self): """Return an iterator over this instance.""" self.index = 0 return self def __next__(self): """ Iterator to loop over grain structures at different mcsteps. Returns ------- tuple A tuple containing the material and grain structure pair. """ if self.index < len(self.gs.keys()): _m_grain_str_pair_ = list(self.gs.values())[self.index] self.index += 1 return _m_grain_str_pair_ else: raise StopIteration def __repr__(self): """ Representation of the grain structure object. Returns ------- str A string representation of the grain structure. """ if self.uigrid.dim == 2: sep1 = 'UPXO 2D.MCGS\n' elif self.uigrid.dim == 3: sep1 = 'UPXO 3D.MCGS\n' GRID = "(A: GRID):: " if hasattr(self, 'uigrid'): GRID += f" x:({self.uigrid.xmin},{self.uigrid.xmax},{self.uigrid.xinc}), " GRID += f" y:({self.uigrid.ymin},{self.uigrid.ymax},{self.uigrid.yinc}), " GRID += f" z:({self.uigrid.zmin},{self.uigrid.zmax},{self.uigrid.zinc})\n" else: GRID += 'Grid parameters not set.\n' # -------------------------------------- SIMPAR = "(B: SIMPAR):: " if hasattr(self, 'uisim'): SIMPAR += f" nstates: {self.uisim.S} mcsteps: {self.uisim.mcsteps}" SIMPAR += f" algorithms: {self.uisim.algo_hops}\n" else: SIMPAR += 'Simulation parameters not set.\n' # -------------------------------------- MESHPAR = "(C: MESHPAR):: " if hasattr(self, 'uimesh'): MESHPAR += f" GB Conformity: {self.uimesh.mesh_gb_conformity}\n" MESHPAR += ' '*15+f"Target FE Software: {self.uimesh.mesh_target_fe_software}" MESHPAR += f" Element type: {self.uimesh.mesh_element_type}" else: MESHPAR += 'Mesh parameters not set yet.\n' # -------------------------------------- return sep1 + GRID + SIMPAR + MESHPAR + '\n' + '-'*60
[docs] def initiate(self, consider_NLM_b=False, consider_NLM_d=False, AR_teevrate = 0, AR_GrainAxis = "-45", display_messages=True): """ Initiate the grain structure object. Parameters ---------- consider_NLM_b : bool, optional Whether to consider Non-Local Matrix of Booleans. The default is False. consider_NLM_d : bool, optional Whether to consider Non-Local Matrix of Distance measures. The default is False. AR_teevrate : float, optional Aspect ratio teev rate. The default is 0. AR_GrainAxis : str, optional Aspect ratio grain axis. The default is "-45". display_messages : bool, optional Whether to display messages. The default is True. """ self._mcsteps_ = [self.uisim.S] # ------------------------------------------- if self.uigrid.dim == 2: self.px_size = self.uigrid.xinc*self.uigrid.yinc self.px_length = (self.uigrid.xinc+self.uigrid.yinc)/2 elif self.uigrid.dim == 3: self.vox_size = self.uigrid.xinc * self.uigrid.yinc * self.uigrid.xinc self.vox_length = (self.uigrid.xinc + self.uigrid.yinc + self.uigrid.zinc)/3 # ---------------------------------------- # Build original co-ordinate grSid self.build_original_coordinate_grid() # ---------------------------------------- # Build original orientation state matrices self.build_original_state_matrix() self.m = list(np.arange(0, self.uisim.mcsteps, self.uiint.mcint_save_at_mcstep_interval, dtype='int')) # Temporarily initiate the tslices. It may get updated in case, # the grain growqth reaches fully_annealed codition !! self.tslices = list(np.arange(0, self.uisim.mcsteps, self.uiint.mcint_save_at_mcstep_interval, dtype='int')) # ---------------------------------------- self.build_ea() # ---------------------------------------- consider_NLM_b_flag, consider_NLM_d_flag = 'no', 'no' if consider_NLM_b: consider_NLM_b_flag = "yes" if consider_NLM_b: consider_NLM_d_flag = "yes" self.NL_dict = dict(NLM_b_dict=dict(flag=consider_NLM_b_flag), NLM_d_dict=dict(flag=consider_NLM_d_flag, func="mexpan", par=(5., 5., 5., 5., 0., 0., 0., 0., 0., 0., 0., 0.), normflag="yes",), ARdetails=dict(teevrate=AR_teevrate, GrainAxis=AR_GrainAxis), ) # ---------------------------------------- # Calculate Non-Local Martix self.build_non_locality_matrix() # ---------------------------------------- # Build appended index array self.AppIndArray() # ---------------------------------------- # Square subset matrix # ssub = self.SquareSubsetMatrix() # ---------------------------------------- from ..viz.artwork_definitions import artwork self.vis = artwork() self.vis.q_Col_Mat(self.uisim.S) # ---------------------------------------- self.setup_transition_probability_rules() # self.vis.s_partitioned_tranition_probabilities(self.uisim.S, self.uisim.s_boltz_prob) # ---------------------------------------- self.tslices_with_prop = [] # ---------------------------------------- self.vizstyles = self.dt_dict.vizstyles_mcgs() self.display_messages = display_messages
[docs] def build_ea(self): """ Build the Euler angle orientation data. Returns ------- None. """ ea1, ea2, ea3 = np.random.uniform([0, 0, 0], [360, 180, 360], (self.uisim.S, 3)).T self.EAPGLB = (ea1, ea2, ea3)
[docs] def build_original_coordinate_grid(self): """ This sets up the original coordinate grid. Original Coordinate Grid: DESCRIPTION OCG inputs 1. dim : Dimensionality of the grid 2. xmin : Minimum x co-ordinate 3. xmax : Maximum x co-ordinate 4. xinc : x co-ordinate increment 5. ymin : Minimum y co-ordinate 6. ymax : Maximum y co-ordinate 7. yinc : y co-ordinate increment 8. zmin : Minimum z co-ordinate 9. zmax : Maximum z co-ordinate 10.zinc : z co-ordinate increment OCG outputs 1. xgr : x co-ordinate grid 2. ygr : y co-ordinate grid 3. zgr : z co-ordinate grid Returns ------- None. """ if self.uigrid.dim == 2: cogrid = np.meshgrid(np.arange(self.uigrid.xmin, self.uigrid.xmax+1, float(self.uigrid.xinc)), np.arange(self.uigrid.ymin, self.uigrid.ymax+1, float(self.uigrid.yinc)), copy=True, sparse=False, indexing='xy') self.xgr, self.ygr, self.zgr = cogrid[0], cogrid[1], 0 # ---------------------------------------- if self.uigrid.dim == 3: xmin, xmax = self.uigrid.xmin, self.uigrid.xmax ymin, ymax = self.uigrid.ymin, self.uigrid.ymax zmin, zmax = self.uigrid.zmin, self.uigrid.zmax xinc = self.uigrid.xinc yinc = self.uigrid.yinc zinc = self.uigrid.zinc xarr = np.arange(xmin, xmax, xinc) yarr = np.arange(ymin, ymax, yinc) zarr = np.arange(zmin, zmax, zinc) #nx = np.floor_divide(xmax-xmin, xinc) #ny = np.floor_divide(ymax-ymin, yinc) #nz = np.floor_divide(zmax-zmin, zinc) #self.xgr, self.ygr, self.zgr = np.mgrid[xmin:xmax:nx*1j, # ymin:ymax:ny*1j, # zmin:zmax:nz*1j] self.xgr, self.ygr, self.zgr = np.meshgrid(xarr, yarr, zarr)
[docs] def build_original_state_matrix(self): """ This sets up the Q-state matrix. Original State Matrix: DESCRIPTION OSM inputs 1. S : No. of orientation states 2. OCG_Size : Size of the original coordinate grid: a 3 element list OSM outputs 1. S : orientation state matrix 2. S_sz0 : dim0 len of S 3. S_sz1 : dim1 len of S 4. S_sz2 : dim2 len of S 5. Svec : S in single row format. IS THIS STILL NEEDED??? Returns ------- None. """ if self.uigrid.dim == 2: OCG_size = (self.xgr.shape[0], self.xgr.shape[1]) # @ 2D grain structure if self.uisim.mcalg[0] not in ('4', '5'): self.S = np.random.randint(1, self.uisim.S+1, size=(OCG_size[0], OCG_size[1])).astype(int) else: self.S = np.random.randint(1, self.uisim.S+1, size=(OCG_size[0], OCG_size[1])).astype(np.float64) elif self.uigrid.dim == 3: OCG_size = (self.xgr.shape[0], self.xgr.shape[1], self.xgr.shape[2]) # @ 3D grain structure self.S = np.random.randint(1, self.uisim.S+1, size=(OCG_size[0], OCG_size[1], OCG_size[2])).astype(int)
[docs] def build_non_locality_matrix(self): """ Construct the non-locality matrix used in some Monte-Carlo simulation. Returns ------- None. """ if self.uigrid.dim == 2: # 2D GRAIN STRUCTURE # Calculate the size of non-local matrix NLM_sz0 = 2*self.uisim.NL+1 NLM_sz1 = 2*self.uisim.NL+1 # Calculate Non-Local Matrix of Booleans if self.NL_dict["NLM_b_dict"]['flag'] in set(['yes', 'y', '1']): NLM_bw = self.IntRules() else: NLM_bw = np.repeat([np.repeat([1.], NLM_sz0, 0)], NLM_sz1, 0) # Calculate Non-Local Matrix of Distance measures if self.NL_dict["NLM_d_dict"]['flag'] in set(['yes', 'y', '1']): NLM_d = self.NLM_dist() else: NLM_d = np.repeat([np.repeat([1.], NLM_sz0, 0)], NLM_sz1, 0) # Calculate the overall Non-Local Matrix self.NLM_nd = NLM_bw * NLM_d self.NLM = np.concatenate(self.NLM_nd) elif self.uigrid.dim == 3: # 3D GRAIN STRUCTURE # Calculate the size of non-local matrix NLM_sz0 = 2*self.uisim.NL+1 NLM_sz1 = 2*self.uisim.NL+1 NLM_sz2 = 2*self.uisim.NL+1 # Calculate Non-Local Matrix of Booleans if self.NL_dict["NLM_b_dict"]['flag'] in set(['yes', 'y', '1']): NLM_bw = self.IntRules() else: NLM_bw = np.repeat([np.repeat([np.repeat([1.], NLM_sz0, 0)], NLM_sz1, 0)], NLM_sz2, 0) # Calculate Non-Local Matrix of Distance measures if self.NL_dict["NLM_d_dict"]['flag'] in set(['yes', 'y', '1']): NLM_d = self.NLM_dist() else: NLM_d = np.repeat([np.repeat([np.repeat([1.], NLM_sz0, 0)], NLM_sz1, 0)], NLM_sz2, 0) # Calculate the overall Non-Local Matrix self.NLM_nd = NLM_bw * NLM_d self.NLM = np.concatenate(np.concatenate(self.NLM_nd))
[docs] def IntRules(self): """ Input arguments [*] ~~Kineticity~~ Nature of partition evolution in Euclidean space OPTIONS: all lower case ("static", "s") ("kinetic0", "k0") -- Default kinetic [*] ~~Dimensionality~~ Number of fundamental axes of simulation Euclidean space OPTIONS: all lower case (1d, 1) (2d, 2) (3d, 3) [*] ~~ARteevrate~~ To describe the strength of partition aspect ratio OPTIONS: all float (following are examples) 0: Aims for equi-axed grains 1: Aims for non unit AR 2: Aims for a higher AR than 1 n: AR_(n) > AR_(n-1) > AR_0 NOTE: Max value limited by no. of pxls across min Gr thickness [*] ~~NL~~ Non-Locality parameter Returns ------- TYPE DESCRIPTION. """ ARteevrate = self.NL_dict['ARdetails']['teevrate'] GrainAxis = self.NL_dict['ARdetails']['GrainAxis'] if self.uisim.kineticity in set(["static", "s", "sta", "kinetic", "k", "kin"]): if self.uisim.kineticity == "static": NLM_sz0 = 2*self.uisim.NL+1 NLM_sz1 = 2*self.uisim.NL+1 NLM_sz2 = 2*self.uisim.NL+1 NLM_sz0 = 3 NLM_sz1 = 3 NLM_sz2 = 3 if self.uigrid.dim == 2: ones = np.repeat([np.repeat([1.], NLM_sz0, 0)], NLM_sz1, 0) arts = np.repeat([np.repeat([float(ARteevrate)], NLM_sz0, 0)], NLM_sz1, 0) if self.uisim.NL == 1: # if ARteevrate == 0: NLM_bw = ones + arts*np.array([[+1., +1., +1.], [+1., +1., +1.], [+1., +1., +1.]]) # elif ARteevrate == 1: _vert_ = ['90', '270', 'V', 'vert', 'vertical'] _hor_ = ['0', '180', 'H', 'hor', 'horizontal'] if GrainAxis.lower() in set([string.lower() for string in _vert_]): NLM_bw = (ones + arts)*np.array([[+0., +0., +0.], [+1., +0., +1.], [+0., +0., +0.]]) print('========== 1 ==========') elif GrainAxis.lower() in set([string.lower() for string in ['+-45', 'x']]): NLM_bw = (ones + arts)*np.array([[+1., +0., +1.], [+0., +0., +0.], [+1., +0., +1.]]) print('========== 2 ==========') elif GrainAxis.lower() in set([string.lower() for string in ['+45', '45']]): NLM_bw = (ones + arts)*np.array([[+1., +0., +0.], [+0., +1., +0.], [+0., +0., +1.]]) print('========== 3 ==========') elif GrainAxis.lower() in set([string.lower() for string in ['-45']]): NLM_bw = (ones + arts)*np.array([[+1, +4., +1.], [+4., +0, +4.], [+1., +4., +1]]) print('========== 4 ==========') elif GrainAxis.lower() in set([string.lower() for string in _hor_]): NLM_bw = (ones + arts)*np.array([[+0.0, +1., +0.0], [+1.0, +0., +1.0], [+0.0, +1., +0.0]]) print('========== 5 ==========') elif self.uisim.NL == 2: NLM_bw = ones+arts*np.array([[+1., +1., +1., +1., +1.], [+1., +1., +1., +1., +1.], [+1., +1., +1., +1., +1.], [+1., +1., +1., +1., +1.], [+1., +1., +1., +1., +1.] ]) else: None elif self.uigrid.dim == 3: ones = np.ones((NLM_sz0, NLM_sz1, NLM_sz2)) arts = float(ARteevrate)*ones arnlm = np.ones((NLM_sz0, NLM_sz1, NLM_sz2)) NLM_bw = (ones + arts)*arnlm elif self.uisim.kineticity == "kinetic": None return NLM_bw else: return print("Please input Kin")
[docs] def AppIndArray(self): """ This sets up appended index array. Appended Index Array: DESCRIPTION AppIndArray inputs 1. NL : Non-Locality 2. OCG_size : List of 3 values. Each is dim along axes 0, 1 and 2 respectively. ~~~~TO BE DONE~~~~ AppIndArray outputs 1. AIA : Appended Index Array (Matlab type array element numbers!) TO KEEP FOR THE MOMENT 2. AIA0 : Appended dim0 Index Array 3. AIA1 : Appended dim1 Index Array 4. AIA2 : Appended dim2 Index Array ~~~~TO BE DONE~~~~ Returns ------- None. Notes ----- [*] Appended Index Array: An array that helps in quick lookup of indices of pixels/cells/voxels in the state matrix S when considering Non-Locality and Wrapped Boundary Conditions. [*] The Appended Index Array is built by appending rows and columns (and layers in 3D) to the original index array of the state matrix S based on the Non-Locality parameter NL. This allows for easy access to neighboring indices without complex boundary checks. [*] In 2D, the AIA is a 2D array where each element corresponds to the linear index of the state matrix S. The AIA0 and AIA1 are derived from the AIA to provide direct access to indices along each dimension. [*] In 3D, the AIA is replaced by a 3D state matrix 'sa' that includes additional layers to account for Non-Locality in all three dimensions, and xinda, yinda, zinda standing for appended index arrays along each dimension. [*] This method is crucial for efficient simulation of grain growth processes, especially when considering interactions beyond immediate neighbors. [*] The implementation currently supports 2D and 3D grids, with the 3D implementation being more complex due to the additional dimension. [*] The method assumes that the state matrix S has already been initialized and that the Non-Locality parameter NL is defined in the simulation parameters. [*] This is crucial for performance optimization in Monte Carlo grain growth simulations and is inspired by similar implementations in MATLAB from the author's previous work PXO standing for 'Poly-Xtal Operations'. It is partly responsible for the speed of UPXO grain growth simulations, which is later further enhanced using Numba JIT compilation in other parts of the code. Notes ----- [*] Original implementation inspiration: MATLAB code from PXO (Poly-Xtal Operations) [*] Performance optimization: Numba JIT compilation in other parts of UPXO [*] Essential for efficient Monte Carlo grain growth simulations [*] Supports 2D and 3D grids with Non-Locality considerations [*] Facilitates quick index lookups in state matrix S [*] Enhances simulation speed and efficiency [*] Critical for handling Wrapped Boundary Conditions [*] DO NOT TOUCH WITHOUT DEEP UNDERSTANDING OF THE METHOD """ if self.uigrid.dim == 2: OCG_size = (self.xgr.shape[0], self.xgr.shape[1]) # Appended Index Array self.AIA = np.arange(np.prod(OCG_size)).reshape((OCG_size[0], OCG_size[1])) dim0 = np.arange(OCG_size[0]) dim1 = np.arange(OCG_size[1]) # Appended Index Array along dimensions 1 & 0: DIM1, DIM0 = np.meshgrid(dim0, dim1, copy=True, sparse=False, indexing='xy') for NLcount in range(0, self.uisim.NL): # Left Edge LE = self.AIA[:, [2*NLcount]] # Right edge RE = self.AIA[:, [len(self.AIA[0]) - 1 - 2*NLcount]] self.AIA = np.concatenate((self.AIA, LE), axis=1) self.AIA = np.concatenate((RE, self.AIA), axis=1) # Top edge TE = self.AIA[[2*NLcount], :] # Bottom edge BE = self.AIA[[len(self.AIA) - 1 - 2*NLcount], :] self.AIA = np.concatenate((BE, self.AIA), axis=0) self.AIA = np.concatenate((self.AIA, TE), axis=0) # Left Edge LE_dim0 = DIM0[:, [2*NLcount]] # Right edge RE_dim0 = DIM0[:, [len(DIM0[0]) - 1 - 2*NLcount]] DIM0 = np.concatenate((DIM0, LE_dim0), axis=1) DIM0 = np.concatenate((RE_dim0, DIM0), axis=1) # Top edge TE_dim0 = DIM0[[2*NLcount], :] # Bottom edge BE_dim0 = DIM0[[len(DIM0) - 1 - 2*NLcount], :] DIM0 = np.concatenate((BE_dim0, DIM0), axis=0) DIM0 = np.concatenate((DIM0, TE_dim0), axis=0) # Left Edge LE_dim1 = DIM1[:, [2*NLcount]] # Right edge RE_dim1 = DIM1[:, [len(DIM1[0]) - 1 - 2*NLcount]] DIM1 = np.concatenate((DIM1, LE_dim1), axis=1) DIM1 = np.concatenate((RE_dim1, DIM1), axis=1) # Top edge TE_dim1 = DIM1[[2*NLcount], :] # Bottom edge BE_dim1 = DIM1[[len(DIM1) - 1 - 2*NLcount], :] DIM1 = np.concatenate((BE_dim1, DIM1), axis=0) DIM1 = np.concatenate((DIM1, TE_dim1), axis=0) self.AIA1 = DIM0.T self.AIA0 = DIM1.T elif self.uigrid.dim == 3: OCG_size = (self.xgr.shape[0], self.xgr.shape[1], self.xgr.shape[2]) self.sa = np.zeros((OCG_size[0]+2, OCG_size[1]+2, OCG_size[2]+2)) self.sa[1:-1, 1:-1, 1:-1] = self.S # ------------------------------------------------------------ # FRONT FACE self.sa[0][1:-1, 1:-1] = self.S[-1] # BACK FACE self.sa[-1][1:-1, 1:-1] = self.S[0] # TOP FACE self.sa[1:-1, 0, 1:-1] = self.S[:, -1, :] # BOTTOM FACE self.sa[1:-1, -1, 1:-1] = self.S[:, 0, :] # LEFT FACE self.sa[1:-1, 1:-1, 0] = self.S[:, :, -1] # RIGHT FACE self.sa[1:-1, 1:-1, -1] = self.S[:, :, 0] # ------------------------------------------------------------ # EDGE @FRONT and TOP self.sa[0, 0, 1:-1] = self.S[-1, -1, :] # EDGE @FRONT and BOTTOM self.sa[0, -1, 1:-1] = self.S[-1, 0, :] # EDGE @FRONT and LEFT self.sa[0, 1:-1, 0] = self.S[-1, :, -1] # EDGE @FRONT and RIGHT self.sa[0, 1:-1, -1] = self.S[-1, :, 0] # ------------------------------------------------------------ # EDGE @BACK and TOP self.sa[-1, 0, 1:-1] = self.S[0, -1, :] # EDGE @BACK and BOTTOM self.sa[-1, -1, 1:-1] = self.S[0, 0, :] # EDGE @BACK and LEFT self.sa[-1, 1:-1, 0] = self.S[0, :, -1] # EDGE @BACK and RIGHT self.sa[-1, 1:-1, -1] = self.S[0, :, 0] # ------------------------------------------------------------ # EDGE @TOP and LEFT self.sa[1:-1, 0, 0] = self.S[:, -1, -1] # EDGE @TOP and RIGHT self.sa[1:-1, 0, 0] = self.S[:, -1, -1] # EDGE @BOTTOM and LEFT self.sa[1:-1, -1, 0] = self.S[:, 0, -1] # EDGE @BOTTOM and RIGHT self.sa[1:-1, -1, -1] = self.S[:, 0, 0] # ------------------------------------------------------------ # VERTEX @FRONT-LEFT-TOP FACES self.sa[0, 0, 0] = self.S[-1, -1, -1] # VERTEX @FRONT-LEFT-BOTTOM FACES self.sa[0, -1, 0] = self.S[-1, 0, -1] # VERTEX @FRONT-RIGHT-BOTTOM FACES self.sa[0, -1, -1] = self.S[-1, 0, 0] # VERTEX @FRONT-RIGHT-TOP FACES self.sa[0, 0, -1] = self.S[-1, -1, 0] # ------------------------------------------------------------ # VERTEX @BACK-LEFT-TOP FACES self.sa[-1, 0, 0] = self.S[0, -1, -1] # VERTEX @BACK-LEFT-BOTTOM FACES self.sa[-1, -1, 0] = self.S[0, 0, -1] # VERTEX @BACK-RIGHT-BOTTOM FACES self.sa[-1, -1, -1] = self.S[0, 0, 0] # VERTEX @FRONT-RIGHT-TOP FACES self.sa[-1, 0, -1] = self.S[0, -1, 0] # ------------------------------------------------------------ self.xind = np.zeros((OCG_size[0], OCG_size[1], OCG_size[2]), dtype=int) self.yind = np.zeros((OCG_size[0], OCG_size[1], OCG_size[2]), dtype=int) self.zind = np.ones((OCG_size[0], OCG_size[1], OCG_size[2]), dtype=int) # ------------------------------------------------------------ tempx = np.tile(np.arange(OCG_size[0]), (OCG_size[0], 1)) for xaxiscount in range(OCG_size[0]): self.xind[xaxiscount] = tempx tempy = np.tile(np.array([np.arange(OCG_size[1])]).T, (1, OCG_size[1])) for yaxiscount in range(OCG_size[1]): self.yind[yaxiscount] = tempy for zaxiscount in range(OCG_size[2]): self.zind[zaxiscount] = float(zaxiscount)*self.zind[zaxiscount] # ------------------------------------------------------------ self.xinda = np.zeros((OCG_size[0]+2, OCG_size[1]+2, OCG_size[2]+2), dtype=int) self.yinda = np.zeros((OCG_size[0]+2, OCG_size[1]+2, OCG_size[2]+2), dtype=int) self.zinda = np.zeros((OCG_size[0]+2, OCG_size[1]+2, OCG_size[2]+2), dtype=int) # ------------------------------------------------------------ self.xinda[1:-1, 1:-1, 1:-1] = self.xind # FRONT FACE self.xinda[0][1:-1, 1:-1] = self.xind[-1] # BACK FACE self.xinda[-1][1:-1, 1:-1] = self.xind[0] # TOP FACE self.xinda[1:-1, 0, 1:-1] = self.xind[:, -1, :] # BOTTOM FACE self.xinda[1:-1, -1, 1:-1] = self.xind[:, 0, :] # LEFT FACE self.xinda[1:-1, 1:-1, 0] = self.xind[:, :, -1] # RIGHT FACE self.xinda[1:-1, 1:-1, -1] = self.xind[:, :, 0] # EDGE @FRONT and TOP self.xinda[0, 0, 1:-1] = self.xind[-1, -1, :] # EDGE @FRONT and BOTTOM self.xinda[0, -1, 1:-1] = self.xind[-1, 0, :] # EDGE @FRONT and LEFT self.xinda[0, 1:-1, 0] = self.xind[-1, :, -1] # EDGE @FRONT and RIGHT self.xinda[0, 1:-1, -1] = self.xind[-1, :, 0] # EDGE @BACK and TOP self.xinda[-1, 0, 1:-1] = self.xind[0, -1, :] # EDGE @BACK and BOTTOM self.xinda[-1, -1, 1:-1] = self.xind[0, 0, :] # EDGE @BACK and LEFT self.xinda[-1, 1:-1, 0] = self.xind[0, :, -1] # EDGE @BACK and RIGHT self.xinda[-1, 1:-1, -1] = self.xind[0, :, 0] # EDGE @TOP and LEFT self.xinda[1:-1, 0, 0] = self.xind[:, -1, -1] # EDGE @TOP and RIGHT self.xinda[1:-1, 0, 0] = self.xind[:, -1, -1] # EDGE @BOTTOM and LEFT self.xinda[1:-1, -1, 0] = self.xind[:, 0, -1] # EDGE @BOTTOM and RIGHT self.xinda[1:-1, -1, -1] = self.xind[:, 0, 0] # VERTEX @FRONT-LEFT-TOPFACES self.xinda[0, 0, 0] = self.xind[-1, -1, -1] # VERTEX @FRONT-LEFT-BOTFACES self.xinda[0, -1, 0] = self.xind[-1, 0, -1] # VERTEX @FRONT-RIGHT-BOTFACES self.xinda[0, -1, -1] = self.xind[-1, 0, 0] # VERTEX @FRONT-RIGHT-TOPFACES self.xinda[0, 0, -1] = self.xind[-1, -1, 0] # VERTEX @BACK-LEFT-TOP FACES self.xinda[-1, 0, 0] = self.xind[0, -1, -1] # VERTEX @BACK-LEFT-BOTFACES self.xinda[-1, -1, 0] = self.xind[0, 0, -1] # VERTEX @BACK-RIGHT-BOTFACES self.xinda[-1, -1, -1] = self.xind[0, 0, 0] # VERTEX @FRONT-RIGHT-TOPFACES self.xinda[-1, 0, -1] = self.xind[0, -1, 0] # ------------------------------------------------------------ self.yinda[1:-1, 1:-1, 1:-1] = self.yind # FRONT FACE self.yinda[0][1:-1, 1:-1] = self.yind[-1] # BACK FACE self.yinda[-1][1:-1, 1:-1] = self.yind[0] # TOP FACE self.yinda[1:-1, 0, 1:-1] = self.yind[:, -1, :] # BOTTOM FACE self.yinda[1:-1, -1, 1:-1] = self.yind[:, 0, :] # LEFT FACE self.yinda[1:-1, 1:-1, 0] = self.yind[:, :, -1] # RIGHT FACE self.yinda[1:-1, 1:-1, -1] = self.yind[:, :, 0] # EDGE @FRONT and TOP self.yinda[0, 0, 1:-1] = self.yind[-1, -1, :] # EDGE @FRONT and BOTTOM self.yinda[0, -1, 1:-1] = self.yind[-1, 0, :] # EDGE @FRONT and LEFT self.yinda[0, 1:-1, 0] = self.yind[-1, :, -1] # EDGE @FRONT and RIGHT self.yinda[0, 1:-1, -1] = self.yind[-1, :, 0] # EDGE @BACK and TOP self.yinda[-1, 0, 1:-1] = self.yind[0, -1, :] # EDGE @BACK and BOTTOM self.yinda[-1, -1, 1:-1] = self.yind[0, 0, :] # EDGE @BACK and LEFT self.yinda[-1, 1:-1, 0] = self.yind[0, :, -1] # EDGE @BACK and RIGHT self.yinda[-1, 1:-1, -1] = self.yind[0, :, 0] # EDGE @TOP and LEFT self.yinda[1:-1, 0, 0] = self.yind[:, -1, -1] # EDGE @TOP and RIGHT self.yinda[1:-1, 0, 0] = self.yind[:, -1, -1] # EDGE @BOTTOM and LEFT self.yinda[1:-1, -1, 0] = self.yind[:, 0, -1] # EDGE @BOTTOM and RIGHT self.yinda[1:-1, -1, -1] = self.yind[:, 0, 0] # VERTEX @FRONT-LEFT-TOP FACES self.yinda[0, 0, 0] = self.yind[-1, -1, -1] # VERTEX @FRONT-LEFT-BOTFACES self.yinda[0, -1, 0] = self.yind[-1, 0, -1] # VERTEX @FRONT-RIGHT-BOTFACES self.yinda[0, -1, -1] = self.yind[-1, 0, 0] # VERTEX @FRONT-RIGHT-TOPFACES self.yinda[0, 0, -1] = self.yind[-1, -1, 0] # VERTEX @BACK-LEFT-TOP FACES self.yinda[-1, 0, 0] = self.yind[0, -1, -1] # VERTEX @BACK-LEFT-BOTFACES self.yinda[-1, -1, 0] = self.yind[0, 0, -1] # VERTEX @BACK-RIGHT-BOTFACES self.yinda[-1, -1, -1] = self.yind[0, 0, 0] # VERTEX @FRONT-RIGHT-TOPFACES self.yinda[-1, 0, -1] = self.yind[0, -1, 0] # ------------------------------------------------------------ self.zinda[1:-1, 1:-1, 1:-1] = self.zind # FRONT FACE self.zinda[0][1:-1, 1:-1] = self.zind[-1] # BACK FACE self.zinda[-1][1:-1, 1:-1] = self.zind[0] # TOP FACE self.zinda[1:-1, 0, 1:-1] = self.zind[:, -1, :] # BOTTOM FACE self.zinda[1:-1, -1, 1:-1] = self.zind[:, 0, :] # LEFT FACE self.zinda[1:-1, 1:-1, 0] = self.zind[:, :, -1] # RIGHT FACE self.zinda[1:-1, 1:-1, -1] = self.zind[:, :, 0] # EDGE @FRONT and TOP self.zinda[0, 0, 1:-1] = self.zind[-1, -1, :] # EDGE @FRONT and BOTTOM self.zinda[0, -1, 1:-1] = self.zind[-1, 0, :] # EDGE @FRONT and LEFT self.zinda[0, 1:-1, 0] = self.zind[-1, :, -1] # EDGE @FRONT and RIGHT self.zinda[0, 1:-1, -1] = self.zind[-1, :, 0] # EDGE @BACK and TOP self.zinda[-1, 0, 1:-1] = self.zind[0, -1, :] # EDGE @BACK and BOTTOM self.zinda[-1, -1, 1:-1] = self.zind[0, 0, :] # EDGE @BACK and LEFT self.zinda[-1, 1:-1, 0] = self.zind[0, :, -1] # EDGE @BACK and RIGHT self.zinda[-1, 1:-1, -1] = self.zind[0, :, 0] # EDGE @TOP and LEFT self.zinda[1:-1, 0, 0] = self.zind[:, -1, -1] # EDGE @TOP and RIGHT self.zinda[1:-1, 0, 0] = self.zind[:, -1, -1] # EDGE @BOTTOM and LEFT self.zinda[1:-1, -1, 0] = self.zind[:, 0, -1] # EDGE @BOTTOM and RIGHT self.zinda[1:-1, -1, -1] = self.zind[:, 0, 0] # VERTEX @FRONT-LEFT-TOP FACES self.zinda[0, 0, 0] = self.zind[-1, -1, -1] # VERTEX @FRONT-LEFT-BOTFACES self.zinda[0, -1, 0] = self.zind[-1, 0, -1] # VERTEX @FRONT-RIGHT-BOTFACES self.zinda[0, -1, -1] = self.zind[-1, 0, 0] # VERTEX @FRONT-RIGHT-TOPFACES self.zinda[0, 0, -1] = self.zind[-1, -1, 0] # VERTEX @BACK-LEFT-TOP FACES self.zinda[-1, 0, 0] = self.zind[0, -1, -1] # VERTEX @BACK-LEFT-BOTFACES self.zinda[-1, -1, 0] = self.zind[0, 0, -1] # VERTEX @BACK-RIGHT-BOTFACES self.zinda[-1, -1, -1] = self.zind[0, 0, 0] # VERTEX @FRONT-RIGHT-TOPFACES self.zinda[-1, 0, -1] = self.zind[0, -1, 0]
[docs] def SquareSubsetMatrix(self): """ This returns the square subset matrix SquareSubsetMatrix: DESCRIPTION SquareSubsetMatrix inputs: 1. NL: Non-Locality parameter SquareSubsetMatrix outputs: 1. ssub Returns ------- ssub : TYPE DESCRIPTION. """ ss_sz0 = 2*self.uisim.NL+1 # S matrix Subset SiZe axis 0, row ss_sz1 = 2*self.uisim.NL+1 # S matrix Subset SiZe axis 1, col ssub = np.zeros((ss_sz0, ss_sz1), dtype=float) return ssub
[docs] def add_gs_data_structure_template(self, m=None, dim=None, study='independent'): """ Add grain statistics data structure template. Parameters ---------- m : int, optional State number. The default is None. dim : int, optional Dimension of the microstructure. The default is None. study : str, optional Type of study. The default is 'independent'. """ from upxo.pxtal.mcgs2_temporal_slice import mcgs2_grain_structure as _GS_ if study == 'independent': if m == 0: self.gs = {m: _GS_(m=m, dim=dim, px_size=self.px_size if dim==2 else self.vox_size, S_total=self.uisim.S, xgr=self.xgr, ygr=self.ygr, uidata=self.__ui, uigrid=self.uigrid, uimesh=self.uimesh, EAPGLB=self.EAPGLB)} else: # THIS BRNACH NEVER REACHED ANYWAYS. TO BE DEPRECATED !! self.gs[m] = _GS_(m=m, dim=dim, px_size=self.px_size, S_total=self.uisim.S, xgr=self.xgr, ygr=self.ygr, uidata=self.__ui, uigrid=self.uigrid, uimesh=self.uimesh, EAPGLB=self.EAPGLB) elif study == 'parameter_sweep': xgr, ygr, npixels = self.uigrid.grid if m == 0: self.gs = {m: _GS_(m=m, dim=self.uigrid.dim, uidata=self.__ui, px_size=self.uigrid.px_size, S_total=self.uisim.S, xgr=self.xgr, ygr=self.ygr, uigrid=self.uigrid, EAPGLB=self.EAPGLB)} else: self.gs[m] = _GS_(m=m, dim=self.uigrid.dim, uidata=self.__ui, px_size=self.px_size, S_total=self.uisim.S, xgr=xgr, ygr=ygr, uigrid=self.uigrid, EAPGLB=self.EAPGLB)
def _setup_grain_properties_dict_(self): """ Store grain properties * areas: Areas (pixels) of all grains: s partitioned * Centroids of all grains: s partitioned * Neighbouring grain IDs (immediate ones) * IDs of immediate neighbours and IDs of neighbours of neighbouring grains Returns ------- None. """ self.__gprop__ = dict(areas=None, centroids=None, neigh_1=[], neigh_2=[[]],) self.gprop = {0: self.__gprop__} def _setup_grainboundaries_dict_(self): """ Store grain boundaries * Grain boundary numbers used as ID * Compulsory: list of list of IDs of all grain boundaries * State wise partitioning * Grain boundary vertices (NOT grain boundary points) Returns ------- None. """ self.__gb__ = dict(ids=None, ind=None, spart=None, vert=None,) self.gb = {0: self.__gb__} def _setup_grainboundaries_properties_dict_(self): """ Store grain boundary propeties * Non-pixel form of total length * Total length calculated from pixel side lengths * Total lengths of all straight lines between grain boundary vertices * Total lengths of all boundary segments between grain boundary vertices * IDs of shared grains * Grain boundary zone Returns ------- None. """ self.__gbprop__ = dict(length_curve=[], length_pixels=[], lengths_straight=[], lengths=[], shared_grains=[], gbz=None, ) self.gbprop = {0: self.__gbprop__}
[docs] def setup_transition_probability_rules(self): """ Set up transition probability rules and estimate T.P Returns ------- None. """ if self.uisim.s_boltz_prob == 'q_unrelated': ''' Generate Boltzmann probabilities unrelated to Q Using: P = exp(-kbf*a) where a is random number between 0 and 1 for each state in S and kbf is boltzmann_temp_factor_max 0 < a < 1 0 < kbf < inf Thus, 0 < P < 1 ''' _a_ = np.random.random(size=self.simpar.S) kbf = self.uisim.boltzmann_temp_factor_max self.uisim.s_boltz_prob = np.exp(-kbf*_a_) elif self.uisim.s_boltz_prob == 'q_related': ''' Generate Boltzmann probabilities related to Q Using: P = exp(-kbf*a) where a is scaled array of state numbers in S and kbf is boltzmann_temp_factor_max 0 < a < boltzmann_temp_factor_max Thus, 0 < P < 1 ''' _a_ = np.arange(self.uisim.S) _a_ = self.uisim.boltzmann_temp_factor_max*_a_/_a_.max() _ = np.random.random(size=self.uisim.S) self.uisim.s_boltz_prob = np.exp(-_a_*_)
[docs] def detect_grains(self, mcsteps=None, kernel_order=2, library='scikit-image', connectivity=26, store_state_ng=True, process_individual_states=False, delta=0, lfiDtype=np.int32, verbose=False): ''' Detect grains in microstructure images using specified image processing library. This method identifies and segments grains in two-dimensional (2D) or three-dimensional (3D) microstructure images based on the provided temporal slices (mcsteps), using either OpenCV or scikit-image libraries for 2D images, and a SciLab-based approach for 3D images. Parameters ---------- mcsteps : int or iterable of int, optional Specifies the temporal slices to analyze. If not provided, all available temporal slices are used. Each temporal slice corresponds to a unique microstructure state. kernel_order : int, optional Specifies the connectivity for grain identification. A higher order increases the connectivity considered during grain detection. Defaults to 2, indicating 8-connectivity in 2D and 26-connectivity in 3D. store_state_ng : bool, optional If True, stores the state of newly generated grains. Defaults to True. library : str, optional Specifies the library to use for grain identification in 2D. Supported libraries are 'opencv' and 'scikit-image'. If not specified, the default library set in the user's ImageGrainSize Configuration (uigsc) is used. UPDATE: 27/12/2025. Use of 'cc3d' for library is implemented. connectivity : int, optional Specifies the connectivity for 2D and 3D grain identification when using 'scikit-image' or 'cc3d' libraries. Defaults to 26 for 3D images. NOTE: This parameter is placed to replace kernel_order in future updates. It is operational only when library is 'cc3d'. Raises ------ TypeError If `mcsteps` is neither an int nor an iterable of int. ValueError If `mcsteps` contains values not available in the temporal slices or if the specified `library` is not supported for the operation. Returns ------- None Modifies the object's state by updating the grain segmentation information for the specified temporal slices. ''' mcsteps_available = list(self.tslices) # All available temporal slices # --------------------------------------------- if not mcsteps: # no value entered, use all available values in tslices mcsteps = mcsteps_available # --------------------------------------------- if not isinstance(mcsteps, int) and not hasattr(mcsteps, '__iter__'): raise TypeError("mcsteps must be an int or an iterable.") # --------------------------------------------- if type(mcsteps) == int: if mcsteps in mcsteps_available: # Single value has been entered for mcsteps mcsteps = [mcsteps] else: # Single value entered and is not one of the available slices raise ValueError(f"mcsteps={mcsteps} not avaialble." f"Value must be from {self.tslices}") # --------------------------------------------- for _ in mcsteps: if _ not in self.tslices: ValueError(f"mcstep={_} not avaialble." f"Value must be from {self.tslices}") # --------------------------------------------- if not library: library = self.uigsc.grain_identification_library # --------------------------------------------- if self.uigrid.dim == 2: if library == 'upxo': print('upxo grain detection has been deprecated..') elif library in dth.opt.ocv_options + dth.opt.ski_options + dth.opt.cc3d_options: print(f"Using {library} for grain identification") from upxo.pxtalops import detect_grains_from_mcstates as get_grains self.gs, state_ng = get_grains.mcgs2d(library=library, gs_dict=self.gs, msteps=mcsteps, kernel_order=kernel_order, store_state_ng=store_state_ng, connectivity=connectivity, process_individual_states=process_individual_states, delta=delta, lfiDtype=lfiDtype, verbose=verbose) else: raise ValueError("Required library should be in, " f"{dth.opt.ocv_options + dth.opt.ski_options + dth.opt.cc3d_options}" f". Receeived: {library}") # --------------------------------------------- if self.uigrid.dim == 3: for m in mcsteps: if m in mcsteps_available: self.find_grains_scilab_ndimage_3d(m) else: print(f'MC temporal slice no {m} invalid. Skipped')
[docs] def detect_grains_v2(self): """Detect grains v2.""" raise NotImplementedError("detect_grains_v2 is not yet implemented.")
[docs] def set_characterization_settings_2d(self, setid=-1): """ Set predefined morphological characterization settings for 2D microstructure images. Parameters ---------- setid : int, optional Predefined setting ID. The default is -1, which corresponds to no characterization. Other options are: - -1: Very basic characterization with only npixels calculated. - 1: Basic characterization with number of pixels and neighboring grain identification. - 2: Standard characterization with bounding box, area, equivalent diameter, compactness, solidity, and neighboring grain identification. - 3: Comprehensive characterization with bounding box, equivalent diameter, compactness, aspect ratio, solidity, morphological orientation, circularity, eccentricity, major and minor axis lengths, Euler number, grain positions, neighboring grain identification, and skim properties. Raises ------ ValueError If `setid` is not -1, 1, 2, or 3. """ self.characterization_flag = setid if setid == -1: characterization_settings = dict(bbox=False, bbox_ex=False, npixels=True, npixels_gb=False, area=False, eq_diameter=False, perimeter=False, perimeter_crofton=False, compactness=False, gb_length_px=False, aspect_ratio=False, solidity=False, morph_ori=False, circularity=False, eccentricity=False, feret_diameter=False, major_axis_length=False, minor_axis_length=False, euler_number=False, char_grain_positions=False, find_neigh=False, char_gb=False, make_skim_prop=False, get_grain_coords=False) if setid == 1: characterization_settings = dict(bbox=False, bbox_ex=False, npixels=True, npixels_gb=False, area=False, eq_diameter=False, perimeter=False, perimeter_crofton=False, compactness=False, gb_length_px=False, aspect_ratio=False, solidity=False, morph_ori=False, circularity=False, eccentricity=False, feret_diameter=False, major_axis_length=False, minor_axis_length=False, euler_number=False, char_grain_positions=False, find_neigh=True, char_gb=False, make_skim_prop=False, get_grain_coords=False) elif setid == 2: characterization_settings = dict(bbox=True, bbox_ex=True, npixels=False, npixels_gb=False, area=True, eq_diameter=True, perimeter=False, perimeter_crofton=False, compactness=True, gb_length_px=False, aspect_ratio=False, solidity=True, morph_ori=False, circularity=False, eccentricity=False, feret_diameter=False, major_axis_length=False, minor_axis_length=False, euler_number=False, char_grain_positions=False, find_neigh=True, char_gb=False, make_skim_prop=True, get_grain_coords=True) elif setid == 3: characterization_settings = dict(bbox=True, bbox_ex=True, npixels=False, npixels_gb=False, area=False, eq_diameter=True, perimeter=False, perimeter_crofton=False, compactness=True, gb_length_px=False, aspect_ratio=True, solidity=True, morph_ori=True, circularity=True, eccentricity=True, feret_diameter=False, major_axis_length=True, minor_axis_length=True, euler_number=True, char_grain_positions=True, find_neigh=True, char_gb=False, make_skim_prop=True, get_grain_coords=True) else: raise ValueError('setid must be -1, 1, 2, or 3.') self.characterization_settings = characterization_settings
[docs] def char_morph_2d(self, M, use_characterization_settings=False, use_version=1, bbox=True, bbox_ex=True, npixels=False, npixels_gb=False, area=True, eq_diameter=True, perimeter=True, perimeter_crofton=False, compactness=True, gb_length_px=False, aspect_ratio=False, solidity=True, morph_ori=False, circularity=False, eccentricity=False, feret_diameter=True, major_axis_length=False, minor_axis_length=False, euler_number=False, append=False, saa=True, throw=False, char_grain_positions=False, find_neigh=True, char_gb=False, make_skim_prop=True, get_grain_coords=True): """ Perform morphological characterization of grains in 2D microstructure images. Parameters ---------- M : int or iterable of int Temporal slice(s) to characterize. use_characterization_settings : bool, optional If True, use predefined characterization settings. The default is False. bbox : bool, optional Calculate bounding box. The default is True. bbox_ex : bool, optional Calculate extended bounding box. The default is True. npixels : bool, optional Calculate number of pixels in each grain. The default is False. npixels_gb : bool, optional Calculate number of pixels in each grain boundary. The default is False. area : bool, optional Calculate area of each grain. The default is True. eq_diameter : bool, optional Calculate equivalent diameter of each grain. The default is True. perimeter : bool, optional Calculate perimeter of each grain. The default is True. perimeter_crofton : bool, optional Calculate perimeter using Crofton method. The default is False. compactness : bool, optional Calculate compactness of each grain. The default is True. gb_length_px : bool, optional Calculate grain boundary length in pixels. The default is False. aspect_ratio : bool, optional Calculate aspect ratio of each grain. The default is False. solidity : bool, optional Calculate solidity of each grain. The default is True. morph_ori : bool, optional Calculate morphological orientation of each grain. The default is False. circularity : bool, optional Calculate circularity of each grain. The default is False. eccentricity : bool, optional Calculate eccentricity of each grain. The default is False. The default is False. feret_diameter : bool, optional Calculate Feret diameter of each grain. The default is True. major_axis_length : bool, optional Calculate major axis length of each grain. The default is False. minor_axis_length : bool, optional Calculate minor axis length of each grain. The default is False. euler_number : bool, optional Calculate Euler number of each grain. The default is False. append : bool, optional If True, append new properties to existing ones. The default is False. saa : bool, optional If True, use spatially aware algorithms. The default is True. throw : bool, optional If True, throw an error if characterization fails. The default is False. char_grain_positions : bool, optional If True, characterize grain positions. The default is False. find_neigh : bool, optional If True, find neighboring grains. The default is True. char_gb : bool, optional If True, characterize grain boundaries. The default is False. make_skim_prop : bool, optional If True, create skim properties. The default is True. get_grain_coords : bool, optional If True, get grain coordinates. The default is True. Returns ------- None Modifies the object's state by updating the morphological properties of grains for the specified temporal slice(s). """ if type(M) == int and M in self.m: print(40*'=') message = f'Characterizing tslice: {M}. Characteriser version {use_version}' print(f'|----- {message} -----|') self.gs[M].char_morph_2d(use_characterization_settings=False, use_version=use_version, bbox=bbox, bbox_ex=bbox_ex, npixels=npixels, npixels_gb=npixels_gb, area=area, eq_diameter=eq_diameter, perimeter=perimeter, perimeter_crofton=perimeter_crofton, compactness=compactness, gb_length_px=gb_length_px, aspect_ratio=aspect_ratio, solidity=solidity, morph_ori=morph_ori, circularity=circularity, eccentricity=eccentricity, feret_diameter=feret_diameter, major_axis_length=major_axis_length, minor_axis_length=minor_axis_length, euler_number=euler_number, append=append, saa=saa, throw=throw, char_grain_positions=char_grain_positions, find_neigh=find_neigh, char_gb=char_gb, make_skim_prop=make_skim_prop, get_grain_coords=get_grain_coords) self.tslices_with_prop.append(M) print(f'....... |-| tslice: {M}: COMPLETE |-|') print(40*'=') elif type(M) == int and M not in self.m: print('Please enter valid temporal slice from PXGS.m') elif type(M) in dth.dt.ITERABLES: for tslice in M: if tslice in self.m: message = f'Characterizing tslice: {tslice}. Characteriser version {use_version}' print(f'|----- {message} -----|') self.gs[tslice].char_morph_2d(use_characterization_settings=False, use_version=use_version, bbox=bbox, bbox_ex=bbox_ex, npixels=npixels, npixels_gb=npixels_gb, area=area, eq_diameter=eq_diameter, perimeter=perimeter, perimeter_crofton=perimeter_crofton, compactness=compactness, gb_length_px=gb_length_px, aspect_ratio=aspect_ratio, solidity=solidity, morph_ori=morph_ori, circularity=circularity, eccentricity=eccentricity, feret_diameter=feret_diameter, major_axis_length=major_axis_length, minor_axis_length=minor_axis_length, euler_number=euler_number, append=append, saa=saa, throw=throw, char_grain_positions=char_grain_positions, find_neigh=find_neigh, char_gb=char_gb, make_skim_prop=make_skim_prop, get_grain_coords=get_grain_coords ) self.tslices_with_prop.append(tslice) else: print(f"tslice = {tslice} not in `PXGS.m`. |-| Ignored |-|")
[docs] def find_grain_areas_fast(self, tslices): """ Quickly find the grain areas without doing anything else. Explanations ------------ Order of grain_areas is that of pxtal.gs[m].gid Parameters ---------- tslices : int or iterable of int Temporal slice(s) to process. Return ------ grain_areas : dict Dictionary with tslice as key and numpy array of grain areas as value. """ grain_areas = {tslice: None for tslice in tslices} print(40*'#') for tslice in tslices: print(40*'-', f'\n Extracting grain areas. tslice={tslice}') grain_areas_tslice = [] for gid in self.gs[tslice].gid: if gid % 100 == 0: print(f'{gid} grains completed.') grain_areas_tslice.append(np.where(self.gs[tslice].lgi == gid)[0].size) grain_areas[tslice] = np.array(grain_areas_tslice) print(40*'#') return grain_areas
[docs] def find_npixels_border_grains_fast(self, tslices): """ Quickly find the number of pixels in border grains without doing anything else. Parameters ---------- tslices : int or iterable of int Temporal slice(s) to process. Return ------ border_grain_npixels : dict Dictionary with tslice as key and numpy array of number of pixels in border grains as value """ if type(tslices) not in dth.ITERABLES: if type(tslices) in dth.NUMBERS: tslices = [tslices] else: raise TypeError('Invalid tslices type specification.') border_grain_npixels = {tslice: None for tslice in tslices} for tslice in tslices: border_grain_npixels[tslice] = self.gs[tslice].find_npixels_border_grains_fast() return border_grain_npixels
[docs] def find_npixels_internal_grains_fast(self, tslices): """ Quickly find the number of pixels in internal grains without doing anything else. Parameters ---------- tslices : int or iterable of int Temporal slice(s) to process. Return ------ internal_grain_npixels : dict Dictionary with tslice as key and numpy array of number of pixels in internal grains as value """ if type(tslices) not in dth.ITERABLES: if type(tslices) in dth.NUMBERS: tslices = [tslices] else: raise TypeError('Invalid tslices type specification.') internal_grain_npixels = {tslice: None for tslice in tslices} for tslice in tslices: internal_grain_npixels[tslice] = self.gs[tslice].find_npixels_internal_grains_fast() return internal_grain_npixels
[docs] def find_border_internal_grains_fast(self, tslices): """ Quickly find the border and internal grains without doing anything else. Parameters ---------- tslices : int or iterable of int Temporal slice(s) to process. Return ------ border_gids : dict Dictionary with tslice as key and numpy array of border grain IDs as value internal_gids : dict Dictionary with tslice as key and numpy array of internal grain IDs as value lgi_border : dict Dictionary with tslice as key and numpy array of lgi of border grains as value lgi_internal : dict Dictionary with tslice as key and numpy array of lgi of internal grains as value """ if type(tslices) not in dth.ITERABLES: tslices = [tslices] border_gids = {tslice: None for tslice in tslices} internal_gids = {tslice: None for tslice in tslices} lgi_border = {tslice: None for tslice in tslices} lgi_internal = {tslice: None for tslice in tslices} for tslice in tslices: a, b, c, d = self.gs[tslice].find_border_internal_grains_fast() border_gids[tslice], internal_gids[tslice] = a, b lgi_border[tslice], lgi_internal[tslice] = c, d return border_gids, internal_gids, lgi_border, lgi_internal
[docs] def hist(self, tslices=None, PROP_NAMES=None, bins=None, kdes=None, kdes_bw=None, stats=None, peaks=False, height=0, prominance=0.2, auto_xbounds=True, auto_ybounds=True, xbounds=[0, 50], ybounds=[0, 0.2]): """ Plot histograms for specified temporal slices and grain properties. Parameters ---------- tslices : int or iterable of int, optional Temporal slice(s) to plot histograms for. If not provided, histograms will be plotted for all temporal slices with available properties. PROP_NAMES : str or iterable of str, optional Grain property/properties to plot histograms for. If not provided, 'npixels' will be used by default. bins : int or iterable of int, optional Number of bins for the histogram. If not provided, default bin size from vizstyles will be used. kdes : bool or iterable of bool, optional Whether to plot kernel density estimates (KDEs). If not provided, KDEs will not be plotted by default. kdes_bw : float or iterable of float, optional Bandwidth adjustment for KDEs. If not provided, default bandwidth will be used. stats : str or iterable of str, optional Statistic to plot ('density', 'count', etc.). If not provided, 'density' will be used by default. peaks : bool, optional Whether to mark peaks on the histogram. Default is False. height : float, optional Height threshold for peak detection. Default is 0. prominance : float, optional Prominence threshold for peak detection. Default is 0.2. auto_xbounds : bool, optional Whether to automatically determine x-axis bounds. Default is True. auto_ybounds : bool, optional Whether to automatically determine y-axis bounds. Default is True. xbounds : list of float, optional Manual x-axis bounds if auto_xbounds is False. Default is [0, 50]. ybounds : list of float, optional Manual y-axis bounds if auto_ybounds is False. Default is [0, 0.2]. Returns ------- None Plots histograms for the specified temporal slices and grain properties. """ # Validate and normalize inputs tslices = self._validate_tslices(tslices) if tslices is None: print("Invalid inputs. No histogram computed. Skipped") return PROP_NAMES = self._normalize_prop_names(PROP_NAMES, len(tslices)) bins = self._normalize_bins(bins, len(tslices)) kdes, kdes_bw = self._normalize_kdes(kdes, kdes_bw, len(tslices)) stats = self._normalize_stats(stats, len(tslices)) # Plot histograms for each temporal slice self._plot_histograms( tslices, PROP_NAMES, bins, kdes, kdes_bw, stats, peaks, height, prominance, auto_xbounds, auto_ybounds, xbounds, ybounds )
def _validate_tslices(self, tslices): """Validate and return list of temporal slices with available properties.""" if tslices is None: tslices_available = [gs.m for gs in self if gs.are_properties_available] return tslices_available if tslices_available else None if isinstance(tslices, int): return [tslices] if type(tslices) in dth.dt.ITERABLES: return tslices print("Invalid tslice datatype. Skipped") return None def _normalize_prop_names(self, prop_names, n_tslices): """Normalize property names to list format.""" if prop_names is None: return ['npixels'] if isinstance(prop_names, str): return [prop_names for _ in range(n_tslices)] if type(prop_names) not in dth.dt.ITERABLES: print("Invalid PROP_NAME. Considering npixels by default") return ['npixels'] return prop_names def _normalize_bins(self, bins, n_tslices): """Normalize bins parameter to list of integers.""" default_bins = self.vizstyles['bins'] if bins is None: return [default_bins for _ in range(n_tslices)] if type(bins) in dth.dt.NUMBERS: return [bins for _ in range(n_tslices)] if type(bins) in dth.dt.ITERABLES: if len(bins) == n_tslices: return [ bin_val if type(bin_val) in dth.dt.NUMBERS else default_bins for bin_val in bins ] return [default_bins for _ in range(n_tslices)] def _normalize_kdes(self, kdes, kdes_bw, n_tslices): """Normalize KDE parameters to lists.""" if kdes is None: return [False] * n_tslices, [None] * n_tslices if isinstance(kdes, bool): kdes_list = [kdes] * n_tslices kdes_bw_list = self._normalize_kdes_bw(kdes_bw, n_tslices) return kdes_list, kdes_bw_list return kdes, kdes_bw def _normalize_kdes_bw(self, kdes_bw, n_tslices): """Normalize KDE bandwidth parameter to list.""" if kdes_bw is None or (type(kdes_bw) not in dth.dt.NUMBERS and type(kdes_bw) not in dth.dt.ITERABLES): return [None] * n_tslices if type(kdes_bw) in dth.dt.NUMBERS: return [kdes_bw] * n_tslices if type(kdes_bw) in dth.dt.ITERABLES: if len(kdes_bw) == n_tslices: return [ bw if type(bw) in dth.dt.NUMBERS else None for bw in kdes_bw ] else: print("len(kdes_bw) must be same as len(kdes)") print("Current set of kdes_bw will result in an error!") print("Please enter valid data.") return [None] * n_tslices def _normalize_stats(self, stats, n_tslices): """Normalize stats parameter to list.""" if stats is None: return ['density'] * n_tslices if isinstance(stats, str): return [stats] * n_tslices if type(stats) not in dth.dt.ITERABLES: print("Invalid stats datatype. Considering density by default") return ['density'] * n_tslices return stats def _plot_histograms(self, tslices, prop_names, bins, kdes, kdes_bw, stats, peaks, height, prominance, auto_xbounds, auto_ybounds, xbounds, ybounds): """Plot histograms for validated inputs.""" for idx, tslice in enumerate(tslices): if tslice not in self.tslices: print(f"Invalid tslice: {tslice}. Skipped") continue if not self.gs[tslice].are_properties_available: print(f"Properties have not been calculated for tslice: {tslice}. Skipped") continue for prop_name in prop_names: if prop_name not in self.gs[tslice].prop.columns: print(f"PROP_NAME: {prop_name} does not exist at tslice: {tslice}. Skipped") continue self.gs[tslice].hist( PROP_NAME=prop_name, bins=bins[idx], kde=kdes[idx], stat=stats[idx], color=self.vizstyles['hist_colors_fill'], edgecolor=self.vizstyles['hist_colors_edge'], alpha=self.vizstyles['hist_colors_fill_alpha'], bw_adjust=kdes_bw[idx], line_kws={ 'color': self.vizstyles['kde_color'], 'lw': self.vizstyles['kde_thickness'], 'ls': '-', }, peaks=peaks, height=height, auto_xbounds=auto_xbounds, auto_ybounds=auto_ybounds, xbounds=xbounds, ybounds=ybounds, prominance=prominance, __stack_call__=True, __tslice__=tslice )
[docs] def find_grains_scilab_ndimage_3d(self, m): """ Find grains in 3D microstructure images using SciPy's ndimage.label function. Parameters ---------- m : int Temporal slice number to process. """ GrStruct = self.gs[m] from scipy.ndimage import label as spndimg_label _S_ = GrStruct.s for i, _s_ in enumerate(np.unique(_S_)): # Mark the presence of this state GrStruct.spart_flag[_s_] = True # Recognize the grains belonging to this state bin_img = (_S_ == _s_).astype(np.uint8) labels, num_labels = spndimg_label(bin_img) if i == 0: GrStruct.lgi = labels else: labels[labels > 0] += GrStruct.lgi.max() GrStruct.lgi = GrStruct.lgi + labels GrStruct.s_gid[_s_] = tuple(np.delete(np.unique(labels), 0)) GrStruct.s_n[_s_-1] = len(GrStruct.s_gid[_s_]) # Get the total number of grains GrStruct.n = np.unique(GrStruct.lgi).size # Generate and store the gid-s mapping GrStruct.gid = list(range(1, GrStruct.n+1)) _gid_s_ = [] for _gs_, _gid_ in zip(GrStruct.s_gid.keys(), GrStruct.s_gid.values()): if _gid_: for __gid__ in _gid_: _gid_s_.append(_gs_) else: pass # _gid_s_.append(0) # Splcing this temporarily. Retain if fully successfull. GrStruct.gid_s = _gid_s_ # Make the output string to print on promnt optput_string_01 = f'Temporal slice number = {m}.' optput_string_02 = f' |||| No. of grains detected = {GrStruct.n}' print(optput_string_01 + optput_string_02)
[docs] @decorators.port_doc('upxo.viz.gsviz', 'see_mcgs2d_features') def plotgs(self, M=[0, 4, 8, 12, 16], cmap='jet', figsize=(5,5), cbtick_incr=2, mbar=True, mbar_length=10, mbar_loc='bot_left'): """ Plot 2D grain structure features. """ from upxo.viz.gsviz import see_mcgs2d_features see_mcgs2d_features(M=M, mcgs2d_upxo=self, cmap=cmap, figsize=figsize, cbtick_incr=cbtick_incr, mbar=mbar, mbar_length=mbar_length, mbar_loc=mbar_loc)
@property def pxtal_length(self): """Pxtal length.""" # Length of the pixelated crystal in the x-direction return self.uigrid.xmax-self.uigrid.xmin @property def pxtal_height(self): """Pxtal height.""" # Height of the pixelated crystal in the y-direction return self.uigrid.ymax-self.uigrid.ymin @property def pxtal_area(self): """Pxtal area.""" # Area of the pixelated crystal return self.pxtal_length*self.pxtal_height
# ---------------------------------------------------------------------
[docs] class mcgs(grid): """ Monte-Carlo Grain Structure class """ def __init__(self, study='independent', input_dashboard='input_dashboard.xls', info_message_display_level='detailed', consider_NLM_b=False, consider_NLM_d=False, AR_factor=0, AR_GrainAxis="-45", display_messages=True): """Initialise the instance.""" super().__init__(study=study, input_dashboard=input_dashboard, consider_NLM_b=consider_NLM_b, consider_NLM_d=consider_NLM_d, AR_teevrate=AR_factor, AR_GrainAxis=AR_GrainAxis, display_messages=display_messages) def __str__(self): """ String representation of the Monte-Carlo Grain Structure object. """ str_1 = f"x:({self.uigrid.xmin},{self.uigrid.xmax},{self.uigrid.xinc}), " str_2 = f"y:({self.uigrid.ymin},{self.uigrid.ymax},{self.uigrid.yinc}), " str_3 = f"z:({self.uigrid.zmin},{self.uigrid.zmax},{self.uigrid.zinc})." return str_1+str_2+str_3 def __att__(self): """ Attribute representation of the Monte-Carlo Grain Structure object. """ return gops.att(self)
[docs] def simulate(self, rsfso=2, user_LIWM=False, LIWM=np.ones((3,3), dtype=np.float32), verbose=True): """ Perform Monte-Carlo simulation to generate grain structures. Parameters ---------- rsfso : int, optional Radius scaling factor for second order neighbors. Default is 2. user_LIWM : bool, optional Flag to indicate if user-defined LIWM is provided. Default is False. LIWM : numpy array, optional Local interaction weight matrix. Default is a 3x3 matrix of ones. verbose : bool, optional Flag to control verbosity of output. Default is True. """ print('\n','Initiating Monte-Carlo simulation') print(f' xmin, xmax, xinc: {self.uigrid.xmin}, {self.uigrid.xmax}, {self.uigrid.xinc}') print(f' ymin, ymax, yinc: {self.uigrid.ymin}, {self.uigrid.ymax}, {self.uigrid.yinc}') print(f' zmin, zmax, zinc: {self.uigrid.zmin}, {self.uigrid.zmax}, {self.uigrid.zinc}') print(f' No. of states: {self.uisim.S}') print(f' Dimensionality: {self.uigrid.dim}') # print(f' Algorithm: {self.uisim.mcalg}', '\n') self.algo_hop = False # Initiate the grain-structure data-structure self.add_gs_data_structure_template(m=0, dim=self.uigrid.dim, study=self.study ) # START THE MONTE-CARLO SIMULATIONS if self.uigrid.dim == 2 and len(self.uisim.algo_hops) == 1: self.algo_hop = False self.start_algo2d_without_hops(rsfso=rsfso, user_LIWM=user_LIWM, LIWM=LIWM) elif self.uigrid.dim == 2 and len(self.uisim.algo_hops) > 1: if self.algo_hop: self.start_algo2d_with_hops(verbose=verbose, user_LIWM=user_LIWM, LIWM=LIWM) else: self.start_algo2d_without_hops(rsfso=rsfso, user_LIWM=user_LIWM, LIWM=LIWM) elif self.uigrid.dim == 3: from scipy.ndimage import label as spndimg_label self.ndimg_label_pck = spndimg_label if len(self.uisim.algo_hops) == 1: self.algo_hop = False self.start_algo3d_without_hops(rsfso=rsfso, verbose=verbose, user_LIWM=user_LIWM, LIWM=LIWM) elif len(self.uisim.algo_hops) > 1: if self.algo_hop: self.start_algo3d_with_hops(verbose=verbose, user_LIWM=user_LIWM, LIWM=LIWM) else: self.start_algo3d_without_hops(rsfso=rsfso, verbose=verbose, user_LIWM=user_LIWM, LIWM=LIWM)
[docs] def start_algo2d_without_hops(self, rsfso=2, user_LIWM=False, LIWM=np.ones((3,3), dtype=np.float32)): """ Start the 2D Monte-Carlo simulation algorithm without hops. Parameters ---------- rsfso : int, optional Radius scaling factor for second order neighbors. Default is 2. user_LIWM : bool, optional Flag to indicate if user-defined LIWM is provided. Default is False. LIWM : numpy array, optional Local interaction weight matrix. Default is a 3x3 matrix of ones. """ _a, _b, _c = self.build_NLM() # Unpack 3 rows of NLM # print('-----------------------') # print(_a) # print(_b) # print(_c) # print('-----------------------') _dm_ = self.display_messages # print(f'UPXO MC Algorithm ID: {self.uisim.mcalg}') # print(type(self.uisim.mcalg)) if self.uisim.mcalg in ('200', '200.0'): import upxo.algorithms.alg200 as alg200 self.gs, fully_annealed = alg200.run(xgr=self.xgr, ygr=self.ygr, zgr=self.zgr, px_size=self.px_size, S=self.S, _a=_a, _b=_b, _c=_c, user_LIWM=user_LIWM, LIWM=LIWM, AIA0=self.AIA0, AIA1=self.AIA1, uisim=self.uisim, uiint=self.uiint, uidata=self.uidata_all, uigrid=self.uigrid, display_messages=_dm_, ) elif self.uisim.mcalg in ('201', '201.0'): import upxo.algorithms.alg201 as alg201 self.gs, fully_annealed = alg201.run(xgr=self.xgr, ygr=self.ygr, zgr=self.zgr, rsfso=rsfso, px_size=self.px_size, S=self.S, _a=_a, _b=_b, _c=_c, user_LIWM=user_LIWM, LIWM=LIWM, AIA0=self.AIA0, AIA1=self.AIA1, uisim=self.uisim, uiint=self.uiint, uidata=self.uidata_all, uigrid=self.uigrid, display_messages=_dm_, ) elif self.uisim.mcalg in ('202', '202.0'): print(" weighted (: ALG-202)") import upxo.algorithms.alg202 as alg202 self.gs, fully_annealed = alg202.run(xgr=self.xgr, ygr=self.ygr, zgr=self.zgr, px_size=self.px_size, S=self.S, _a=_a, _b=_b, _c=_c, user_LIWM=user_LIWM, LIWM=LIWM, AIA0=self.AIA0, AIA1=self.AIA1, uisim=self.uisim, uiint=self.uiint, uidata=self.uidata_all, uigrid=self.uigrid, display_messages=_dm_, ) # Update the tslices to accommodate the fully_annealed condition, if # it is achieved during simulation if fully_annealed['fully_annealed']: self.tslices = list(self.gs.keys())
[docs] def start_algo2d_with_hops(self, user_LIWM=False, LIWM=np.ones((3,3), dtype=np.float32)): """Start algo2d with hops.""" raise NotImplementedError("start_algo2d_with_hops is not yet implemented.")
[docs] def start_algo3d_without_hops(self, rsfso=3, user_LIWM=False, LIWM=np.ones((3,3,3), dtype=np.float32), verbose=True): """ Start the 3D Monte-Carlo simulation algorithm without hops. Parameters ---------- rsfso : int, optional Radius scaling factor for second order neighbors. Default is 3. user_LIWM : bool, optional Flag to indicate if user-defined LIWM is provided. Default is False. LIWM : numpy array, optional Local interaction weight matrix. Default is a 3x3x3 matrix of ones. verbose : bool, optional Flag to control verbosity of output. Default is True. """ if self.uisim.mcalg == '300a': print("Using ALG-300a") import upxo.algorithms.alg300a as alg300a print('////////////////////////////////') _alg300a_ = alg300a.mc_iterations_3d_alg300a self.gs, fully_annealed = _alg300a_(S=self.S, xinda=self.xinda, yinda=self.yinda, zinda=self.zinda, vox_size=self.vox_size, uidata=self.uidata_all, uigrid=self.uigrid, uisim=self.uisim, uiint=self.uiint, verbose=verbose, ndimg_label_pck=self.ndimg_label_pck) elif self.uisim.mcalg == '300b': print("Using ALG-300b") import upxo.algorithms.alg300b as alg300b print('////////////////////////////////') _alg300b_ = alg300b.mc_iterations_3d_alg300b self.gs, fully_annealed = _alg300b_(S=self.S, xinda=self.xinda, yinda=self.yinda, zinda=self.zinda, vox_size=self.vox_size, uidata=self.uidata_all, uigrid=self.uigrid, uisim=self.uisim, uiint=self.uiint, verbose=verbose, ndimg_label_pck=self.ndimg_label_pck) elif self.uisim.mcalg == '301.0': print("Using ALG-301") import upxo.algorithms.alg301 as alg301 print('////////////////////////////////') _alg301_ = alg301.mc_iterations_3d_alg301 self.gs, fully_annealed = _alg301_(S=self.S, xinda=self.xinda, yinda=self.yinda, zinda=self.zinda, vox_size=self.vox_size, uidata=self.uidata_all, uigrid=self.uigrid, uisim=self.uisim, uiint=self.uiint, verbose=verbose, ndimg_label_pck=self.ndimg_label_pck) elif self.uisim.mcalg == '302.0': print("Using ALG-302") import upxo.algorithms.alg302 as alg302 print('////////////////////////////////') _alg302_ = alg302.mc_iterations_3d_alg302 self.gs, fully_annealed = _alg302_(S=self.S, xinda=self.xinda, yinda=self.yinda, zinda=self.zinda, rsfso=rsfso, vox_size=self.vox_size, uidata=self.uidata_all, uigrid=self.uigrid, uisim=self.uisim, uiint=self.uiint, verbose=verbose, ndimg_label_pck=self.ndimg_label_pck)
[docs] def build_NLM(self): """ Build the Non-Locality Matrix (NLM) based on the non-locality level. Returns ------- NLM : numpy array The constructed Non-Locality Matrix. """ if self.uisim.NL == 1: NLM_00 = self.NLM_nd[0, 0] NLM_01 = self.NLM_nd[0, 1] NLM_02 = self.NLM_nd[0, 2] NLM_10 = self.NLM_nd[1, 0] NLM_11 = self.NLM_nd[1, 1] NLM_12 = self.NLM_nd[1, 2] NLM_20 = self.NLM_nd[2, 0] NLM_21 = self.NLM_nd[2, 1] NLM_22 = self.NLM_nd[2, 2] NLM = np.array([[NLM_00, NLM_01, NLM_02], [NLM_10, NLM_11, NLM_12], [NLM_20, NLM_21, NLM_22]]) elif self.uisim.NL == 2: NLM_00 = self.NLM_nd[0, 0] NLM_01 = self.NLM_nd[0, 1] NLM_02 = self.NLM_nd[0, 2] NLM_03 = self.NLM_nd[0, 3] NLM_04 = self.NLM_nd[0, 4] NLM_10 = self.NLM_nd[1, 0] NLM_11 = self.NLM_nd[1, 1] NLM_12 = self.NLM_nd[1, 2] NLM_13 = self.NLM_nd[1, 3] NLM_14 = self.NLM_nd[1, 4] NLM_20 = self.NLM_nd[2, 0] NLM_21 = self.NLM_nd[2, 1] NLM_22 = self.NLM_nd[2, 2] NLM_23 = self.NLM_nd[2, 3] NLM_24 = self.NLM_nd[2, 4] NLM_30 = self.NLM_nd[3, 0] NLM_31 = self.NLM_nd[3, 1] NLM_32 = self.NLM_nd[3, 2] NLM_33 = self.NLM_nd[3, 3] NLM_34 = self.NLM_nd[3, 4] NLM_40 = self.NLM_nd[4, 0] NLM_41 = self.NLM_nd[4, 1] NLM_42 = self.NLM_nd[4, 2] NLM_43 = self.NLM_nd[4, 3] NLM_44 = self.NLM_nd[4, 4] NLM = np.array([[NLM_00, NLM_01, NLM_02, NLM_03, NLM_04], [NLM_10, NLM_11, NLM_12, NLM_13, NLM_14], [NLM_20, NLM_21, NLM_22, NLM_23, NLM_24], [NLM_30, NLM_31, NLM_32, NLM_33, NLM_34], [NLM_40, NLM_41, NLM_42, NLM_43, NLM_44]]) return NLM
[docs] def NLM_elements(self): """Nlm elements.""" # Build the Non-Locality Matrix _a, _b, _c = self.build_NLM() # Unpack 3 rows of NLM NLM_00, NLM_01, NLM_02 = _a # Unpack 3 colms of 1st row NLM_10, NLM_11, NLM_12 = _b # Unpack 3 colms of 2nd row NLM_20, NLM_21, NLM_22 = _c # Unpack 3 colms of 3rd row return NLM_00, NLM_01, NLM_02, NLM_10, NLM_11, NLM_12, NLM_20, NLM_21, NLM_22