==================== Preparation - finish ==================== ''' ''' ============ Core process ============ ''' lprint("Input data") lprint("==========") # construct Lat_A = BravaisLattice(brava, pa, dim=dim) Lat_M = BravaisLattice(bravm, pm, dim=dim) E_A = Lat_A.base() E_M = Lat_M.base() E_Minv = la.inv(E_M) dist = lambda x: Cauchy_dist(x, E_A, E_Minv) dist_vec = lambda xs: Cauchy_dist_vec(xs, E_A, E_Minv) C_A = Lat_A.conventional_trans() C_M = Lat_M.conventional_trans() LG_A = Lat_A.lattice_group().matrices() LG_M = Lat_M.lattice_group().matrices()
def lat_cor(ibrava, pbrava, ibravm, pbravm, **kwargs): ''' find the optimal lattice invarient shear move E1 as close as possible to E2 allowed kwargs: - dim: dimension of the Bravais lattice, default is 3, the only other option is 2 - nsol: number of solutions - vol_th: volume change threshold - dist: choose from "Cauchy", "Ericksen" and "strain", default is "Cauchy" - disp: level of detail of printing, default is 2 0 => no print 1 => two lattices and solution 2 => progress over HNFs 3 => more info in the lat_opt 4 => all info in the lat_opt (not implemented) - maxiter: maximum iteration depth, default is 3 - lat_opt_par: a map function for parallel excution of lat_opt, for example define a pool in __main__ first: pool = multiprocessing.Pool() then define the mapping function def lat_opt_par(args, chuncksize): return pool.map(lat_opt_unpack, args, chuncksize) - poolsize: maximum number of parallel processes. default is the number of cpus finally call lat_cor in __main__ ''' ''' =========== Preparation =========== ''' # start the timer t_start = timer() # read kwargs def readkw(field, default): return kwargs[field] if field in kwargs else default nsol = readkw('nsol', 1) vol_th = readkw('vol_th', 0.1) disp = readkw('disp', 1) dim = readkw('dim', 3) dist = readkw('dist', 'Cauchy') def lprint(msg, lev=1): # print with level if disp >= lev: print(msg) if lev == 1: logger.info(msg) elif lev >= 2: logger.debug(msg) ''' ==================== Preparation - finish ==================== ''' ''' ============ Core process ============ ''' lprint("Input data") lprint("==========") # construct Lat_A = BravaisLattice(ibrava, pbrava, dim=dim) Lat_M = BravaisLattice(ibravm, pbravm, dim=dim) E_A = Lat_A.base() E_M = Lat_M.base() #E_Mr = LLL(E_M) #chi = np.array(np.rint(la.inv(E_Mr).dot(E_M)), dtype='int') E_Mr = E_M chi = np.eye(dim, dtype=np.int) C_A = Lat_A.getConventionalTrans() C_M = Lat_M.getConventionalTrans() LG_A = Lat_A.special_lattice_group().matrices() LG_M = Lattice(E_Mr).special_lattice_group().matrices() lprint(" - Austenite lattice:", 1) lprint(" {:s}".format(str(Lat_A)), 1) lprint(" - Martensite lattice:", 1) lprint(" {:s}".format(str(Lat_M)), 1) lprint("", 1) # Determine the list of Hermite Normal Forms r = la.det(E_M)/la.det(E_A) # ratio of the volumes of the unit cells # find all the sizes of sublattices corresponding to # volume changes less than the threshold "vol_th" vf = np.arange( math.ceil((1-vol_th)*r), # lower bound math.floor((1+vol_th)*r)+1, # upper bound dtype='int') if len(vf) == 0: vf = (np.round(r),) hnfs = [h for i in vf for h in HermiteNormalForms(i, dim)] lprint('The ratio between the volume of unit cells is {:g}.'.format(r), 2) lprint('The volume change threshold is {:.2%}.'.format(vol_th), 2) lprint('So, the possible size(s) of austenite sublattice is (are) {:s}.'.format(str(vf)), 2) lprint('There are {:d} Hermite Normal Forms in total.' .format(len(hnfs)), 2) lprint('Stripping down symmetry related Hermite Normal Forms ...', 1) if 'reduce_hnfs_par' in kwargs: from multiprocessing import cpu_count poolsize = cpu_count() if len(hnfs) > poolsize * 20: def divide_work(W, size): # divide W into sub-groups of at most size ngrp = int(math.ceil(len(W)/float(size))) return [W[int(g*size):min(int((g+1)*size), len(W))] for g in xrange(ngrp)] reduce_hnfs_par = kwargs['reduce_hnfs_par'] lprint("Dividing {:d} into {:d} groups for parallel processing ...".format(len(hnfs), poolsize)) subhnfs = divide_work(hnfs, int(math.ceil(len(hnfs)/poolsize))) # parallel reduce par_reduce = reduce_hnfs_par(zip(subhnfs, [LG_A]*len(subhnfs))) from itertools import chain hnfs = list(chain.from_iterable(par_reduce)) lprint("Merging results from parallel reducing in the master process ...") hnfs = reduce_hnfs([hnfs, LG_A, lprint]) else: hnfs = reduce_hnfs([hnfs, LG_A, lprint]) lprint('Found {:d} symmetry-independent HNFs in total.'.format(len(hnfs)), 1) lprint('Looking for {:d} best lattice correspondence(s).'.format(nsol), 2) # Search for the best unit cell for each Hermite Normal Form lprint('Search over {:d} sublattices'.format(len(hnfs)), 1) lprint('===========================', 1) # options for lat_opt options = { 'nsol': nsol, 'dist': dist, 'disp': disp - 1, 'SOLG2': LG_M } if 'maxiter' in kwargs: options['maxiter'] = kwargs['maxiter'] if 'miniter' in kwargs: options['miniter'] = kwargs['miniter'] EXT_SOLS2 = {} def ext_sols(l): """add l to extended solution dictionary""" ls = (q1.dot(l).dot(q2) for q1 in LG_A for q2 in LG_M) for c in ls: EXT_SOLS2[c.tostring()] = True def add_sol(sols, s): """add new solution s to the list of solutions""" ltot = np.dot(s['h'], s['l']) if not ltot.tostring() in EXT_SOLS2: # if True: pos = len(sols) for k in xrange(len(sols)): if sols[k]['d'] >= s['d']: pos = k break sols.insert(pos, s) ext_sols(ltot) truncate_sols(sols, nsol) def truncate_sols(sols, nsol): """truncate sols to number of solutions""" if len(sols) > nsol: t = 0 for i in xrange(len(sols)): if i < len(sols) - nsol and sols[- 1 - i]['d'] > sols[nsol - 1]['d']: t += 1 else: break for _ in xrange(t): sols[-1] = None sols.pop(-1) def merge_sols(sols, new): """merge new solutions into old ones""" if len(sols) == 0: for s in new: add_sol(sols, s) else: for s in xrange(len(new)): if new[s]['d'] <= sols[-1]['d']: add_sol(sols, new[s]) else: return sols = [] if 'lat_opt_par' in kwargs: from multiprocessing import cpu_count # parallel execution lprint('{:d} HNFs are being solved in parallel ...'.format(len(hnfs)), 1) poolsize = cpu_count() if 'poolsize' not in kwargs else kwargs['poolsize'] lat_opt_par = kwargs['lat_opt_par'] args = ((np.dot(E_A, h), E_Mr, options, ih) for ih, h in enumerate(hnfs)) # initialize async task list async_results = [] nextarg = next(args, None) while nextarg is not None and len(async_results) < poolsize: async_results.append(lat_opt_par([nextarg])) nextarg = next(args, None) while len(async_results) > 0: ares = async_results.pop(0) if ares.ready(): res = ares.get() lprint("Merging the solutions from HNF No. {:d} ...".format(res[0] + 1), 2) merge_sols(sols, [{'d': d, 'l': l, 'h': hnfs[res[0]]} for l, d in zip(res[1]['lopt'], res[1]['dopt'])]) # append new calculation to the end of task list if nextarg is not None: async_results.append(lat_opt_par([nextarg])) nextarg = next(args, None) else: # put it back to the end of the tast list async_results.append(ares) # sleep(0.0) else: ''' serial processing ''' # sequential execution lprint('{:d} HNFs are being solved ...'.format(len(hnfs)), 1) for ih, h in enumerate(hnfs): options['ihnf'] = ih res = lat_opt(np.dot(E_A, h), E_Mr, **options) merge_sols(sols, [{'d': d, 'l': l, 'h': hnfs[ih]} for l, d in zip(res['lopt'], res['dopt'])]) lprint('Done.', 1) # change from E_Mr back to E_M for sol in sols: sol['l'] = sol['l'].dot(chi) cor = np.dot(np.dot(la.inv(C_A), np.dot(sol['h'], sol['l'])), C_M) sol['cor'] = copy.copy(cor) # Cauchy-Born deformation gradient: F.EA.H.L = EM F = np.dot(E_M, la.inv(np.dot(E_A, np.dot(sol['h'], sol['l'])))) C = np.dot(F.T, F) [lams, V] = la.eig(C) # spectrum decomposition lams = np.sqrt(np.real(lams)) U = np.dot(np.dot(V, np.diag(lams)), la.inv(V)) sol['u'] = copy.copy(U) lams = np.sort(lams) sol['lams'] = copy.copy(lams) lprint("\nFinally {:d} / {:d} solutions are found.".format(len(sols), nsol), 3) ''' ===================== Core process - finish ===================== ''' ''' ============ Print result ============ ''' # Print and save the results lprint('Print and save the results') lprint('==========================') output = _sols_tostr(sols, nsol, dim, dist) if disp > 0: print(output) for line in output.split("\n"): logger.info(line) ''' ===================== Print result - finish ===================== ''' # timer lprint("All done in {:g} secs.".format(timer()-t_start), 1) return sols
def lat_cor(brava, pa, bravm, pm, **kwargs): """ find the optimal lattice invarient shear move E1 as close as possible to E2 allowed kwargs: - dim: dimension of the Bravais lattice, default is 3, the only other option is 2 - nsol: number of solutions - slice_sz: size of each slice of L's for vectorized calculation, default is 1000 - disp: level of detail of printing, default is 2 0 => no print 1 => two lattices and solution 2 => progress over HNFs 3 => more info in the lat_opt 4 => all info in the lat_opt (not implemented) - logfile: name of the logfile - loglevel: level of the logging """ ''' =========== Preparation =========== ''' # start the timer t_start = timer() # read kwargs def readkw(field, default): return kwargs[field] if field in kwargs else default nsol = readkw('nsol', 1) vol_th = readkw('vol_th', 0.1) disp = readkw('disp', 1) dim = readkw('dim', 3) slice_sz = readkw('slice_sz', 100) distName = readkw('dist', 'Cauchy') def lprint(msg, lev=1): # print with level if disp >= lev: print(msg) if lev == 1: logger.info(msg) elif lev >= 2: logger.debug(msg) # setup logging loglevel = readkw('loglevel', logging.INFO) if 'logfile' in kwargs: # FORMAT = '[%(levelname)s] %(asctime)-15s %(name)s: %(message)s' FORMAT = '%(message)s' logfile = readkw('logfile', 'untitled.log') logging.basicConfig(filename=logfile, filemode='w', level=loglevel, format=FORMAT) ''' ==================== Preparation - finish ==================== ''' ''' ============ Core process ============ ''' lprint("Input data") lprint("==========") # construct Lat_A = BravaisLattice(brava, pa, dim=dim) Lat_M = BravaisLattice(bravm, pm, dim=dim) E_A = Lat_A.base() E_M = Lat_M.base() E_Minv = la.inv(E_M) dist = lambda x: Cauchy_dist(x, E_A, E_Minv) dist_vec = lambda xs: Cauchy_dist_vec(xs, E_A, E_Minv) C_A = Lat_A.conventional_trans() C_M = Lat_M.conventional_trans() LG_A = Lat_A.lattice_group().matrices() LG_M = Lat_M.lattice_group().matrices() def eqlatcor(l): L = l.reshape(dim, dim) return set( tuple(M1.dot(L).dot(M2).flatten()) for M1 in LG_A for M2 in LG_M) lprint(" - Austenite lattice:") lprint(" {:s}".format(str(Lat_A))) lprint(" - Martensite lattice:") lprint(" {:s}".format(str(Lat_M))) lprint("") # minimum gamma gamma = lambda x: 3 + 2 * np.sqrt(3 * x) + x lprint("Searching") lprint("=========") C1 = np.tensordot(E_A, E_Minv, axes=([], [])).transpose(0, 3, 1, 2) C2 = np.tensordot(C1, C1, axes=([1], [1]))\ .transpose(0, 3, 1, 2, 4, 5)\ .reshape(dim**2, dim**2, dim**2) D = np.tensordot(C2, C2, axes=([0], [0])) lprint('finding alpha by quartic minimization ...', 2) alpha = quart_min(D) lprint('alpha = {:>9.6f}'.format(alpha), 2) # start searching def add_sol(sol, sols, ext_sols): # if can add to the solution list if not tuple(sol['l']) in ext_sols: # insert the solution and update EXT_SOLS idx = np.searchsorted([s['d'] for s in sols], sol['d']) if la.det(sol['l'].reshape(dim, dim)) != 0: sols.insert(idx, sol) ext_sols = ext_sols.union(eqlatcor(sol['l'])) if len(sols) > nsol: while sols[-1]['d'] > sols[nsol - 1]['d']: # sols.pop() delSol = sols.pop() ext_sols = ext_sols.difference(eqlatcor(delSol['l'])) return sols, ext_sols, sols[-1]['d'] def update_sol(ls, sols, ext_sols, dmax): dmax_ref = dmax ds = dist_vec(ls) while len(ds) > 0: minidx = np.argmin(ds) dmin = ds[minidx] ds = np.delete(ds, minidx) lmin = ls[minidx].reshape(dim**2) ls = np.delete(ls, minidx, axis=0) if dmin <= dmax: sols, ext_sols, dmax = add_sol({ 'l': lmin, 'd': dmin }, sols, ext_sols) else: break updated = (dmax < dmax_ref) return updated, sols, ext_sols, dmax # initialization L0 = np.floor(la.inv(E_A).dot(E_M)).astype(np.int) SOLS = [] EXT_SOLS = set() DMAX = float('inf') for _ in xrange(20 * nsol): shift = np.random.random_integers(-1, 1, (dim, dim)) L = L0 + shift l = L.reshape(dim**2) while tuple(l) in EXT_SOLS or not (1 - vol_th < la.det(L0 + shift) < 1 + vol_th): shift = np.random.random_integers(-1, 1, (dim, dim)) L = L0 + shift l = L.reshape(dim**2) sol = {'l': l, 'd': dist(l)} SOLS, EXT_SOLS, DMAX = add_sol(sol, SOLS, EXT_SOLS) rmax = lambda d: (gamma(d) / alpha)**0.25 maxRadius = rmax(DMAX) lprint('start searching ...') updated = True checkpoint = np.zeros(dim**2, np.int) # iteration while updated: updated = False g = vec_generator(maxRadius, dim**2, checkpoint) lprint( 'searching radius = {:>9.6f}, max distance = {:>9.6f}, starting at {:s}' .format(maxRadius, DMAX, str(checkpoint)), 2) ary = np.array(list(itertools.islice(g, slice_sz))) while len(ary) > 0: updated, SOLS, EXT_SOLS, DMAX = update_sol(ary, SOLS, EXT_SOLS, DMAX) if updated: maxRadius = rmax(DMAX) checkpoint = np.array(next(g)) break else: ary = np.array(list(itertools.islice(g, slice_sz))) for sol in SOLS: eqls = eqlatcor(sol['l']) sol['l'] = sol['l'].reshape(dim, dim) for l in eqls: L = np.array(l).reshape(dim, dim) if la.det(L) > 0: sol['l'] = L break cor = np.dot(np.dot(la.inv(C_A), sol['l']), C_M) sol['cor'] = cor[:] # Cauchy-Born deformation gradient: F.EA.H.L = EM F = np.dot(E_M, la.inv(np.dot(E_A, sol['l']))) C = np.dot(F.T, F) [lams, V] = la.eig(C) # spectrum decomposition lams = np.sqrt(np.real(lams)) U = np.dot(np.dot(V, np.diag(lams)), la.inv(V)) sol['u'] = U[:] lams = np.sort(lams) sol['lams'] = lams[:] lprint("Finally {:d} / {:d} solutions are found.".format(len(SOLS), nsol), 3) lprint("") ''' ===================== Core process - finish ===================== ''' ''' ============ Print result ============ ''' # Print and save the results lprint('Print and save the results') lprint('==========================') output = _sols_tostr(SOLS, nsol, dim, distName) if disp > 0: print(output) for line in output.split("\n"): logger.info(line) ''' ===================== Print result - finish ===================== ''' # timer lprint("All done in {:g} secs.".format(timer() - t_start), 1) return SOLS
def lat_cor(brava, pa, bravm, pm, **kwargs): """ find the optimal lattice invarient shear move E1 as close as possible to E2 allowed kwargs: - dim: dimension of the Bravais lattice, default is 3, the only other option is 2 - nsol: number of solutions - slice_sz: size of each slice of L's for vectorized calculation, default is 1000 - disp: level of detail of printing, default is 2 0 => no print 1 => two lattices and solution 2 => progress over HNFs 3 => more info in the lat_opt 4 => all info in the lat_opt (not implemented) - logfile: name of the logfile - loglevel: level of the logging """ ''' =========== Preparation =========== ''' # start the timer t_start = timer() # read kwargs def readkw(field, default): return kwargs[field] if field in kwargs else default nsol = readkw('nsol', 1) vol_th = readkw('vol_th', 0.1) disp = readkw('disp', 1) dim = readkw('dim', 3) slice_sz = readkw('slice_sz', 100) distName = readkw('dist', 'Cauchy') def lprint(msg, lev=1): # print with level if disp >= lev: print(msg) if lev == 1: logger.info(msg) elif lev >= 2: logger.debug(msg) # setup logging loglevel =readkw('loglevel', logging.INFO) if 'logfile' in kwargs: # FORMAT = '[%(levelname)s] %(asctime)-15s %(name)s: %(message)s' FORMAT = '%(message)s' logfile = readkw('logfile', 'untitled.log') logging.basicConfig(filename=logfile, filemode='w', level=loglevel, format=FORMAT) ''' ==================== Preparation - finish ==================== ''' ''' ============ Core process ============ ''' lprint("Input data") lprint("==========") # construct Lat_A = BravaisLattice(brava, pa, dim=dim) Lat_M = BravaisLattice(bravm, pm, dim=dim) E_A = Lat_A.base() E_M = Lat_M.base() E_Minv = la.inv(E_M) dist = lambda x: Cauchy_dist(x, E_A, E_Minv) dist_vec = lambda xs: Cauchy_dist_vec(xs, E_A, E_Minv) C_A = Lat_A.conventional_trans() C_M = Lat_M.conventional_trans() LG_A = Lat_A.lattice_group().matrices() LG_M = Lat_M.lattice_group().matrices() def eqlatcor(l): L = l.reshape(dim, dim) return set(tuple(M1.dot(L).dot(M2).flatten()) for M1 in LG_A for M2 in LG_M) lprint(" - Austenite lattice:") lprint(" {:s}".format(str(Lat_A))) lprint(" - Martensite lattice:") lprint(" {:s}".format(str(Lat_M))) lprint("") # minimum gamma gamma = lambda x: 3 + 2 * np.sqrt(3 * x) + x lprint("Searching") lprint("=========") C1 = np.tensordot(E_A, E_Minv, axes=([], [])).transpose(0, 3, 1, 2) C2 = np.tensordot(C1, C1, axes=([1], [1]))\ .transpose(0, 3, 1, 2, 4, 5)\ .reshape(dim**2, dim**2, dim**2) D = np.tensordot(C2, C2, axes=([0], [0])) lprint('finding alpha by quartic minimization ...', 2) alpha = quart_min(D) lprint('alpha = {:>9.6f}'.format(alpha), 2) # start searching def add_sol(sol, sols, ext_sols): # if can add to the solution list if not tuple(sol['l']) in ext_sols: # insert the solution and update EXT_SOLS idx = np.searchsorted([s['d'] for s in sols], sol['d']) if la.det(sol['l'].reshape(dim, dim)) != 0: sols.insert(idx, sol) ext_sols = ext_sols.union(eqlatcor(sol['l'])) if len(sols) > nsol: while sols[-1]['d'] > sols[nsol-1]['d']: # sols.pop() delSol = sols.pop() ext_sols = ext_sols.difference(eqlatcor(delSol['l'])) return sols, ext_sols, sols[-1]['d'] def update_sol(ls, sols, ext_sols, dmax): dmax_ref = dmax ds = dist_vec(ls) while len(ds) > 0: minidx = np.argmin(ds) dmin = ds[minidx] ds = np.delete(ds, minidx) lmin = ls[minidx].reshape(dim**2) ls = np.delete(ls, minidx, axis=0) if dmin <= dmax: sols, ext_sols, dmax = add_sol({'l': lmin, 'd': dmin}, sols, ext_sols) else: break updated = (dmax < dmax_ref) return updated, sols, ext_sols, dmax # initialization L0 = np.floor(la.inv(E_A).dot(E_M)).astype(np.int) SOLS = [] EXT_SOLS = set() DMAX = float('inf') for _ in xrange(20 * nsol): shift = np.random.random_integers(-1, 1, (dim, dim)) L = L0 + shift l = L.reshape(dim**2) while tuple(l) in EXT_SOLS or not (1 - vol_th < la.det(L0 + shift) < 1 + vol_th): shift = np.random.random_integers(-1, 1, (dim, dim)) L = L0 + shift l = L.reshape(dim**2) sol = {'l': l, 'd': dist(l)} SOLS, EXT_SOLS, DMAX = add_sol(sol, SOLS, EXT_SOLS) rmax = lambda d: (gamma(d) / alpha) ** 0.25 maxRadius = rmax(DMAX) lprint('start searching ...') updated = True checkpoint = np.zeros(dim**2, np.int) # iteration while updated: updated = False g = vec_generator(maxRadius, dim**2, checkpoint) lprint('searching radius = {:>9.6f}, max distance = {:>9.6f}, starting at {:s}'.format( maxRadius, DMAX, str(checkpoint) ), 2) ary = np.array(list(itertools.islice(g, slice_sz))) while len(ary) > 0: updated, SOLS, EXT_SOLS, DMAX = update_sol(ary, SOLS, EXT_SOLS, DMAX) if updated: maxRadius = rmax(DMAX) checkpoint = np.array(next(g)) break else: ary = np.array(list(itertools.islice(g, slice_sz))) for sol in SOLS: eqls = eqlatcor(sol['l']) sol['l'] = sol['l'].reshape(dim, dim) for l in eqls: L = np.array(l).reshape(dim, dim) if la.det(L) > 0: sol['l'] = L break cor = np.dot(np.dot(la.inv(C_A), sol['l']), C_M) sol['cor'] = cor[:] # Cauchy-Born deformation gradient: F.EA.H.L = EM F = np.dot(E_M, la.inv(np.dot(E_A, sol['l']))) C = np.dot(F.T, F) [lams, V] = la.eig(C) # spectrum decomposition lams = np.sqrt(np.real(lams)) U = np.dot(np.dot(V, np.diag(lams)), la.inv(V)) sol['u'] = U[:] lams = np.sort(lams) sol['lams'] = lams[:] lprint("Finally {:d} / {:d} solutions are found.".format(len(SOLS), nsol), 3) lprint("") ''' ===================== Core process - finish ===================== ''' ''' ============ Print result ============ ''' # Print and save the results lprint('Print and save the results') lprint('==========================') output = _sols_tostr(SOLS, nsol, dim, distName) if disp > 0: print(output) for line in output.split("\n"): logger.info(line) ''' ===================== Print result - finish ===================== ''' # timer lprint("All done in {:g} secs.".format(timer()-t_start), 1) return SOLS
''' ==================== Preparation - finish ==================== ''' ''' ============ Core process ============ ''' lprint("Input data") lprint("==========") # construct Lat_A = BravaisLattice(brava, pa, dim=dim) Lat_M = BravaisLattice(bravm, pm, dim=dim) E_A = Lat_A.base() E_M = Lat_M.base() E_Minv = la.inv(E_M) dist = lambda x: Cauchy_dist(x, E_A, E_Minv) dist_vec = lambda xs: Cauchy_dist_vec(xs, E_A, E_Minv) C_A = Lat_A.conventional_trans() C_M = Lat_M.conventional_trans() LG_A = Lat_A.lattice_group().matrices() LG_M = Lat_M.lattice_group().matrices()
def load_input(self): self.lprint("Input data") self.lprint("==========") # construct Lat_A = BravaisLattice(self.brava, self.pa, dim=self.dim) Lat_M = BravaisLattice(self.bravm, self.pm, dim=self.dim) E_A = Lat_A.base() E_M = Lat_M.base() self.E_A = E_A self.E_M = E_M E_Minv = la.inv(E_M) self.dist = lambda x: Cauchy_dist(x, E_A, E_Minv) self.dist_vec = lambda xs: Cauchy_dist_vec(xs, E_A, E_Minv) C_A = Lat_A.conventional_trans() C_M = Lat_M.conventional_trans() LG_A = Lat_A.lattice_group().matrices() LG_M = Lat_M.lattice_group().matrices() def eqlatcor(l): L = l.reshape(self.dim, self.dim) return set(tuple(M1.dot(L).dot(M2).flatten()) for M1 in LG_A for M2 in LG_M) self.lprint(" - Austenite lattice:") self.lprint(" {:s}".format(str(Lat_A))) self.lprint(" - Martensite lattice:") self.lprint(" {:s}".format(str(Lat_M))) self.lprint("") # minimum gamma self.gamma = lambda x: 3 + 2 * np.sqrt(3 * x) + x C1 = np.tensordot(E_A, E_Minv, axes=([], [])).transpose(0, 3, 1, 2) C2 = np.tensordot(C1, C1, axes=([1], [1])) \ .transpose(0, 3, 1, 2, 4, 5) \ .reshape(self.dim ** 2, self.dim ** 2, self.dim ** 2) D = np.tensordot(C2, C2, axes=([0], [0])) self.lprint('finding alpha by quartic minimization ...', 2) self.alpha = quart_min(D) self.lprint('alpha = {:>9.6f}'.format(self.alpha), 2) # start searching def add_sol(sol, sols, ext_sols): # if can add to the solution list if not tuple(sol['l']) in ext_sols: # insert the solution and update EXT_SOLS idx = np.searchsorted([s['d'] for s in sols], sol['d']) if la.det(sol['l'].reshape(self.dim, self.dim)) != 0: sols.insert(idx, sol) ext_sols = ext_sols.union(eqlatcor(sol['l'])) if len(sols) > self.nsol: while sols[-1]['d'] > sols[self.nsol - 1]['d']: delSol = sols.pop() ext_sols = ext_sols.difference(eqlatcor(delSol['l'])) return sols, ext_sols, sols[-1]['d'] self.add_sol = add_sol def update_sol(ls, sols, ext_sols, dmax): dmax_ref = dmax ds = self.dist_vec(ls) while len(ds) > 0: minidx = np.argmin(ds) dmin = ds[minidx] ds = np.delete(ds, minidx) lmin = ls[minidx].reshape(self.dim ** 2) ls = np.delete(ls, minidx, axis=0) if dmin <= dmax: sols, ext_sols, dmax = add_sol({'l': lmin, 'd': dmin}, sols, ext_sols) else: break updated = (dmax < dmax_ref) return updated, sols, ext_sols, dmax self.update_sol = update_sol