def full(self, **kwargs): """convert a tucker representation to a full tensor object A = CORE (*1) Basis1 (*2) Basis2 (*3) Basis3 ..., with (*n) means n-mode product. from paper "A MULTILINEAR SINGULAR VALUE DECOMPOSITION" by LIEVEN DE LATHAUWER , BART DE MOOR , AND JOOS VANDEWALLE """ # if core is a single scalar value, make it in a n-D array shape if np.prod(self.core.shape) == 1: self.core = np.reshape(self.core, tuple(self.r)) if self.Fourier: res = self.fourier() else: res = self val = res.core.copy() for i in range(self.order): val = nModeProduct(val, res.basis[i].T, i) T = Tensor(name=res.name, val=val, order=0, N=val.shape, Fourier=False, **kwargs) if self.Fourier: T.fourier() return T
def full(self, **kwargs): "return a full tensor object" if self.order == 2: if self.Fourier: res = self.fourier(copy=True) else: res = self # generate Tensor in real domain val = np.einsum('i,ik,il->kl', res.core, res.basis[0], res.basis[1]) kwargsT = dict(name=res.name, val=val, order=0, N=val.shape, Fourier=False, fft_form='c') kwargsT.update(kwargs) T = Tensor(**kwargsT) if self.Fourier: T.fourier() return T else: raise NotImplementedError()
def test_solvers(self): print('\nChecking solvers...') dim = 2 n = 5 N = n * np.ones(dim, dtype=np.int) _, hG1Nt, _ = scalar_tensor(N, Y=np.ones(dim)) FN = DFT(name='FN', inverse=False, N=N) FiN = DFT(name='FiN', inverse=True, N=N) G1N = Operator(name='G1', mat=[[FiN, hG1Nt, FN]]) A = Tensor(name='A', val=np.einsum('ij,...->ij...', np.eye(dim), 1. + 10. * np.random.random(N)), order=2, N=N, multype=21) E = np.zeros((dim, ) + dim * (n, )) E[0] = 1. # set macroscopic loading E = Tensor(name='E', val=E, order=1, N=N) GAfun = Operator(name='GA', mat=[[G1N, A]]) GAfun.define_operand(E) B = GAfun(-E) x0 = E.copy(name='x0') x0.val[:] = 0 par = { 'tol': 1e-10, 'maxiter': int(1e3), 'alpha': 0.5 * (1. + 10.), 'eigrange': [1., 10.] } # reference solution X, _ = linear_solver(Afun=GAfun, B=B, x0=x0, par=par, solver='CG') prt.disable() for solver in ['CG', 'scipy_cg', 'richardson', 'chebyshev']: x, _ = linear_solver(Afun=GAfun, B=B, x0=x0, par=par, solver=solver) self.assertAlmostEqual(0, norm(X.val - x.val), delta=1e-8, msg=solver) prt.enable() print('...ok')
def homog_Ga_full_potential(Aga, pars): Nbar = Aga.N # double grid number N = np.array((np.array(Nbar) + 1) / 2, dtype=np.int) dim = Nbar.__len__() F2 = DFT(name='FN', inverse=False, N=Nbar) # discrete Fourier transform (DFT) iF2 = DFT(name='FiN', inverse=True, N=Nbar) # inverse DFT P = get_preconditioner(N, pars) E = np.zeros(dim) E[0] = 1 # macroscopic load EN = Tensor(name='EN', N=Nbar, shape=(dim, ), Fourier=False) # constant trig. pol. EN.set_mean(E) def DFAFGfun(X): assert (X.Fourier) FAX = F2(Aga * iF2(grad(X).enlarge(Nbar))) FAX = FAX.project(N) return -div(FAX) B = div(F2(Aga(EN)).decrease(N)) x0 = Tensor(N=N, shape=(), Fourier=True) # initial approximation to solvers PDFAFGPfun = lambda Fx: P * DFAFGfun(P * Fx) PB = P * B tic = Timer(name='CG (potential)') iPU, info = linear_solver(solver='CG', Afun=PDFAFGPfun, B=PB, x0=x0, par=pars.solver, callback=None) tic.measure() print(('iterations of CG={}'.format(info['kit']))) print(('norm of residuum={}'.format(info['norm_res']))) R = PB - PDFAFGPfun(iPU) print(('norm of residuum={} (from definition)'.format(R.norm()))) Fu = P * iPU X = iF2(grad(Fu).project(Nbar)) AH = Aga(X + EN) * (X + EN) return Struct(AH=AH, e=X, Fu=Fu, info=info, time=tic.vals[0][0])
def get_preconditioner(N, pars): hGrad = grad_tensor(N, pars.Y) k2 = np.einsum('i...,i...', hGrad.val, np.conj(hGrad.val)).real k2[mean_index(N)] = 1. return Tensor(name='P', val=1. / k2**0.5, order=0, N=N, Fourier=True, multype=00)
def test_solvers(self): print('\nChecking solvers...') dim=2 n=5 N = n*np.ones(dim, dtype=np.int) _, hG1Nt, _ = scalar_tensor(N, Y=np.ones(dim)) FN=DFT(name='FN', inverse=False, N=N) FiN=DFT(name='FiN', inverse=True, N=N) G1N=Operator(name='G1', mat=[[FiN, hG1Nt, FN]]) A=Tensor(name='A', val=np.einsum('ij,...->ij...', np.eye(dim), 1.+10.*np.random.random(N)), order=2, N=N, multype=21) E=np.zeros((dim,)+dim*(n,)); E[0] = 1. # set macroscopic loading E=Tensor(name='E', val=E, order=1, N=N) GAfun=Operator(name='GA', mat=[[G1N, A]]) GAfun.define_operand(E) B=GAfun(-E) x0=E.copy(name='x0') x0.val[:]=0 par={'tol': 1e-10, 'maxiter': int(1e3), 'alpha': 0.5*(1.+10.), 'eigrange':[1., 10.]} # reference solution X,_=linear_solver(Afun=GAfun, B=B, x0=x0, par=par, solver='CG') prt.disable() for solver in ['CG', 'scipy_cg', 'richardson', 'chebyshev']: x,_=linear_solver(Afun=GAfun, B=B, x0=x0, par=par, solver=solver) self.assertAlmostEqual(0, norm(X.val-x.val), delta=1e-8, msg=solver) prt.enable() print('...ok')
def __call__(self, x): self.iter += 1 if isinstance(x, np.ndarray): if isinstance(self.B, VecTri): X = VecTri(val=np.reshape(x, self.B.dN())) elif isinstance(self.B, VecTri): X = Tensor(val=np.reshape(x, self.B.dN()), shape=self.B.shape) else: X = x res = self.B - self.A(X) self.res_norm.append(res.norm()) return
def homog_Ga_full(Aga, pars): Nbar = Aga.N N = np.array((np.array(Nbar) + 1) / 2, dtype=np.int) dim = Nbar.__len__() Y = np.ones(dim) _, Ghat, _ = proj.scalar(N, Y) Ghat2 = Ghat.enlarge(Nbar) F2 = DFT(name='FN', inverse=False, N=Nbar) # discrete Fourier transform (DFT) iF2 = DFT(name='FiN', inverse=True, N=Nbar) # inverse DFT G1N = Operator(name='G1', mat=[[iF2, Ghat2, F2]]) # projection in original space PAfun = Operator(name='FiGFA', mat=[[G1N, Aga]]) # lin. operator for a linear system E = np.zeros(dim) E[0] = 1 # macroscopic load EN = Tensor(name='EN', N=Nbar, shape=(dim, ), Fourier=False) # constant trig. pol. EN.set_mean(E) x0 = Tensor(N=Nbar, shape=(dim, ), Fourier=False) # initial approximation to solvers B = PAfun(-EN) # right-hand side of linear system tic = Timer(name='CG (gradient field)') X, info = linear_solver(solver='CG', Afun=PAfun, B=B, x0=x0, par=pars.solver, callback=None) tic.measure() AH = Aga(X + EN) * (X + EN) return Struct(AH=AH, X=X, time=tic.vals[0][0], pars=pars)
def full(self, **kwargs): """ convert TT to a full tensor object """ if self.Fourier: res = self.fourier() else: res = self val = vector.full(res) # Tensor with the default fft_form for full tensor T = Tensor(name=res.name, val=val, order=0, N=val.shape, Fourier=False, **kwargs) if self.Fourier: T.fourier() return T
def evaluate(self, coord, tensor=True): """ Evaluate material at coordinates (coord). Parameters ---------- material : material definition coord : numpy.array coordinates where material coefficients are evaluated Returns ------- A : numpy.array material coefficients at coordinates (coord) """ N = coord.shape[1:] if 'fun' in self.conf: fun = self.conf['fun'] A_val = fun(coord) shp = A_val.shape[:A_val.ndim - len(N)] else: shp = self.conf['vals'][0].shape A_val = np.zeros(shp + N) topos = self.get_topologies(coord) astr = 'abcdefgh' istr = 'ijklmnopqrst' for ii in np.arange(len(self.conf['inclusions'])): stroper = '{0},{1}->{0}{1}'.format(astr[:len(shp)], istr[:len(N)]) A_val += np.einsum(stroper, self.conf['vals'][ii], topos[ii]) if tensor: multypes = {'2': 21, '4': 42} return Tensor(name='A_GaNi', val=A_val, order=len(shp), N=coord[0].shape, Y=self.Y, multype=multypes['{}'.format(len(shp))], Fourier=False, origin='c') else: return Matrix(name='A_GaNi', val=A_val, Fourier=False)
def homog_GaNi_full_potential(Agani, Aga, pars): N = Agani.N # double grid number dim = N.__len__() F = DFT(name='FN', inverse=False, N=N) # discrete Fourier transform (DFT) iF = DFT(name='FiN', inverse=True, N=N) # inverse DFT P = get_preconditioner(N, pars) E = np.zeros(dim) E[0] = 1 # macroscopic load EN = Tensor(name='EN', N=N, shape=(dim, ), Fourier=False) # constant trig. pol. EN.set_mean(E) def DFAFGfun(X): assert (X.Fourier) FAX = F(Agani * iF(grad(X))) return -div(FAX) B = div(F(Agani(EN))) x0 = Tensor(N=N, shape=(), Fourier=True) # initial approximation to solvers PDFAFGPfun = lambda Fx: P * DFAFGfun(P * Fx) PB = P * B tic = Timer(name='CG (potential)') iPU, info = linear_solver(solver='CG', Afun=PDFAFGPfun, B=PB, x0=x0, par=pars.solver, callback=None) tic.measure() print(('iterations of CG={}'.format(info['kit']))) print(('norm of residuum={}'.format(info['norm_res']))) Fu = P * iPU if Aga is None: # GaNi homogenised properties print('!!!!! homogenised properties are GaNi only !!!!!') XEN = iF(grad(Fu)) + EN AH = Agani(XEN) * XEN else: Nbar = 2 * np.array(N) - 1 iF2 = DFT(name='FiN', inverse=True, N=Nbar) # inverse DFT XEN = iF2(grad(Fu).project(Nbar)) + EN.project(Nbar) AH = Aga(XEN) * XEN return Struct(AH=AH, Fu=Fu, info=info, time=tic.vals[0][0], pars=pars)
def test_even(self): print('\nChecking Tensors with even grid points...') for dim, n, fft_form in itertools.product([2,3], [4,5], fft_forms): msg='Tensors with: dim={}, n={}, fft_form={}'.format(dim, n, fft_form) N=dim*(n,) M=tuple(2*np.array(N)) u=Tensor(name='test', shape=(), N=N, Fourier=False, fft_form=fft_form) u.randomize() Fu=u.fourier(copy=True) FuM=Fu.project(M) uM=FuM.fourier() if n%2 == 0: self.assertGreaterEqual(u.norm(), FuM.norm(), msg=msg) self.assertGreaterEqual(u.norm(componentwise=True), FuM.norm(componentwise=True), msg=msg) self.assertGreaterEqual(u.norm(), uM.norm(), msg=msg) self.assertGreaterEqual(u.norm(componentwise=True), uM.norm(componentwise=True), msg=msg) else: self.assertAlmostEqual(u.norm(), FuM.norm(), msg=msg) self.assertAlmostEqual(u.norm(componentwise=True), FuM.norm(componentwise=True), msg=msg) self.assertAlmostEqual(u.norm(), uM.norm(), msg=msg) self.assertAlmostEqual(u.norm(componentwise=True), uM.norm(componentwise=True), msg=msg) self.assertAlmostEqual(0, u.mean()-FuM.mean(), msg=msg) self.assertAlmostEqual(u.mean(), uM.mean(), msg=msg) # testing that that interpolation on double grid have exactly the same values slc=tuple(u.order*[slice(None),]+[slice(0,M[i],2) for i in range(dim)]) self.assertAlmostEqual(0, np.linalg.norm(u.val-uM.val[slc]), msg=msg) print('...ok')
def get_A_Ga(self, Nbar, primaldual='primal', order=-1, P=None): """ Returns stiffness matrix for scheme with exact integration. """ if order == -1: if 'order' in self.conf: order = self.conf['order'] else: raise ValueError('The material order is undefined!') elif order not in [None, 'exact', 0, 1]: raise ValueError('Wrong material order (%s)!' % str(order)) if order in [None, 'exact']: # inclusion-based composite shape_funs = self.get_shape_functions(Nbar) val = np.zeros(self.conf['vals'][0].shape + shape_funs[0].shape) for ii in range(len(self.conf['inclusions'])): if primaldual is 'primal': Aincl = self.conf['vals'][ii] elif primaldual is 'dual': Aincl = np.linalg.inv(self.conf['vals'][ii]) val += np.einsum('ij...,k...->ijk...', Aincl, shape_funs[ii]) name = 'A_Ga' else: # grid-based composite if P is None and 'P' in self.conf: P = self.conf['P'] coord = Grid.get_coordinates(P, self.Y) vals = self.evaluate(coord) if primaldual is 'dual': vals = vals.inv() h = self.Y / P if order in [0, 'constant']: Wraw = get_weights_con(h, Nbar, self.Y) elif order in [1, 'bilinear']: Wraw = get_weights_lin(h, Nbar, self.Y) val = np.zeros(vals.shape + tuple(Nbar)) for m, n in itertools.product(*(list(range(d)) for d in vals.shape)): hAM0 = np.prod(P) * cfftnc(vals[m, n], P) if np.allclose(P, Nbar): hAM = hAM0 elif np.all(np.greater_equal(P, Nbar)): hAM = decrease(hAM0, Nbar) elif np.all(np.less(P, Nbar)): factor = np.ceil(np.array(Nbar, dtype=np.float64) / 2 / P) hAM0per = np.tile(hAM0, 2 * np.array(factor, dtype=np.int) + 1) hAM = decrease(hAM0per, Nbar) else: msg = """This combination of double N (%s) and P (%s) " implemented.""" % (str(Nbar), str(P)) raise NotImplementedError(msg) val[m, n] = np.real(icfftnc(Wraw * hAM, Nbar)) name = 'A_Ga_o{0}_P{1}'.format(order, np.array(P).max()) return Tensor(name=name, val=val, N=Nbar, order=2, Y=self.Y, multype=21, Fourier=False, origin='c').shift()
def elasticity(N, Y, NyqNul=True, tensor=True, fft_form=fft_form_default): """ Projection matrix on a space of admissible strain fields INPUT = N : ndarray of e.g. stiffness coefficients d : dimension; d = 2 D : dimension in engineering notation; D = 3 Y : the size of periodic unit cell OUTPUT = G1h,G1s,G2h,G2s : projection matrices of size DxDxN """ if fft_form in ['r']: fft_form_r = True fft_form = 0 else: fft_form_r = False xi = Grid.get_xil(N, Y, fft_form=fft_form) N = np.array(N, dtype=np.int) d = N.size D = int(d * (d + 1) / 2) if NyqNul: Nred = get_Nodd(N) else: Nred = N xi2 = [] for ii in range(d): xi2.append(xi[ii]**2) num = np.zeros(np.hstack([d, d, Nred])) norm2_xi = np.zeros(Nred) for mm in np.arange(d): # diagonal components Nshape = np.ones(d, dtype=np.int) Nshape[mm] = Nred[mm] Nrep = np.copy(Nred) Nrep[mm] = 1 num[mm][mm] = np.tile(np.reshape(xi2[mm], Nshape), Nrep) # numerator norm2_xi += num[mm][mm] norm4_xi = norm2_xi**2 ind_center = mean_index(Nred, fft_form=fft_form) # avoid division by zero norm2_xi[ind_center] = 1 norm4_xi[ind_center] = 1 for m in np.arange(d): # upper diagonal components for n in np.arange(m + 1, d): NshapeM = np.ones(d, dtype=np.int) NshapeM[m] = Nred[m] NrepM = np.copy(Nred) NrepM[m] = 1 NshapeN = np.ones(d, dtype=np.int) NshapeN[n] = Nred[n] NrepN = np.copy(Nred) NrepN[n] = 1 num[m][n] = np.tile(np.reshape(xi[m], NshapeM), NrepM) \ * np.tile(np.reshape(xi[n], NshapeN), NrepN) # G1h = np.zeros([D,D]).tolist() G1h = np.zeros(np.hstack([D, D, Nred])) G1s = np.zeros(np.hstack([D, D, Nred])) IS0 = np.zeros(np.hstack([D, D, Nred])) mean = np.zeros(np.hstack([D, D, Nred])) Lamh = np.zeros(np.hstack([D, D, Nred])) S = np.zeros(np.hstack([D, D, Nred])) W = np.zeros(np.hstack([D, D, Nred])) WT = np.zeros(np.hstack([D, D, Nred])) for m in np.arange(d): S[m][m] = 2 * num[m][m] / norm2_xi for n in np.arange(d): G1h[m][n] = num[m][m] * num[n][n] / norm4_xi Lamh[m][n] = np.ones(Nred) / d Lamh[m][n][ind_center] = 0 for m in np.arange(D): IS0[m][m] = np.ones(Nred) IS0[m][m][ind_center] = 0 mean[m][m][ind_center] = 1 if d == 2: S[0][2] = 2**0.5 * num[0][1] / norm2_xi S[1][2] = 2**0.5 * num[0][1] / norm2_xi S[2][2] = np.ones(Nred) S[2][2][ind_center] = 0 G1h[0][2] = 2**0.5 * num[0][0] * num[0][1] / norm4_xi G1h[1][2] = 2**0.5 * num[0][1] * num[1][1] / norm4_xi G1h[2][2] = 2 * num[0][0] * num[1][1] / norm4_xi for m in np.arange(d): for n in np.arange(d): W[m][n] = num[m][m] / norm2_xi W[2][m] = 2**.5 * num[0][1] / norm2_xi elif d == 3: for m in np.arange(d): S[m + 3][m + 3] = 1 - num[m][m] / norm2_xi S[m + 3][m + 3][ind_center] = 0 for m in np.arange(d): for n in np.arange(m + 1, d): S[m + 3][n + 3] = num[m][n] / norm2_xi G1h[m + 3][n + 3] = num[m][m] * num[n][n] / norm4_xi for m in np.arange(d): for n in np.arange(d): ind = sp.setdiff1d(np.arange(d), [n]) S[m][n + 3] = (0 == (m == n)) * 2**.5 * num[ind[0]][ind[1]] / norm2_xi G1h[m][n + 3] = 2**.5 * num[m][m] * num[ind[0]][ind[1]] / norm4_xi W[m][n] = num[m][m] / norm2_xi W[n + 3][m] = 2**.5 * num[ind[0]][ind[1]] / norm2_xi for m in np.arange(d): for n in np.arange(d): ind_m = sp.setdiff1d(np.arange(d), [m]) ind_n = sp.setdiff1d(np.arange(d), [n]) G1h[m+3][n+3] = 2*num[ind_m[0]][ind_m[1]] \ * num[ind_n[0]][ind_n[1]] / norm4_xi # symmetrization for n in np.arange(D): for m in np.arange(n + 1, D): S[m][n] = S[n][m] G1h[m][n] = G1h[n][m] for m in np.arange(D): for n in np.arange(D): G1s[m][n] = S[m][n] - 2 * G1h[m][n] WT[m][n] = W[n][m] G2h = 1. / (d - 1) * (d * Lamh + G1h - W - WT) G2s = IS0 - G1h - G1s - G2h if tensor: G0 = Tensor(name='hG0', val=mean, order=2, N=N, Fourier=True, multype=21, fft_form=fft_form) G1h = Tensor(name='hG1h', val=G1h, order=2, N=N, Fourier=True, multype=21, fft_form=fft_form) G1s = Tensor(name='hG1s', val=G1s, order=2, N=N, Fourier=True, multype=21, fft_form=fft_form) G2h = Tensor(name='hG2h', val=G2h, order=2, N=N, Fourier=True, multype=21, fft_form=fft_form) G2s = Tensor(name='hG2s', val=G2s, order=2, N=N, Fourier=True, multype=21, fft_form=fft_form) else: G0 = Matrix(name='hG0', val=mean, Fourier=True) G1h = Matrix(name='hG1h', val=G1h, Fourier=True) G1s = Matrix(name='hG1s', val=G1s, Fourier=True) G2h = Matrix(name='hG2h', val=G2h, Fourier=True) G2s = Matrix(name='hG2s', val=G2s, Fourier=True) if NyqNul: G0 = G0.enlarge(N) G1h = G1h.enlarge(N) G1s = G1s.enlarge(N) G2h = G2h.enlarge(N) G2s = G2s.enlarge(N) if fft_form_r: for tensor in [G0, G1h, G1s, G2h, G2s]: tensor.set_fft_form(fft_form='r') tensor.val = 1. / np.prod(tensor.N) * tensor.val return G0, G1h, G1s, G2h, G2s
def elasticity(problem): """ Homogenization of linear elasticity. Parameters ---------- problem : object """ print(' ') pb = problem print(pb) # Fourier projections _, hG1hN, hG1sN, hG2hN, hG2sN = proj.elasticity(pb.solve['N'], pb.Y, NyqNul=True) del _ if pb.solve['kind'] is 'GaNi': Nbar = pb.solve['N'] elif pb.solve['kind'] is 'Ga': Nbar = 2*pb.solve['N'] - 1 hG1hN = hG1hN.enlarge(Nbar) hG1sN = hG1sN.enlarge(Nbar) hG2hN = hG2hN.enlarge(Nbar) hG2sN = hG2sN.enlarge(Nbar) FN = DFT(name='FN', inverse=False, N=Nbar) FiN = DFT(name='FiN', inverse=True, N=Nbar) G1N = Operator(name='G1', mat=[[FiN, hG1hN + hG1sN, FN]]) G2N = Operator(name='G2', mat=[[FiN, hG2hN + hG2sN, FN]]) for primaldual in pb.solve['primaldual']: tim = Timer(name='primal-dual') print('\nproblem: ' + primaldual) solutions = np.zeros(pb.shape).tolist() results = np.zeros(pb.shape).tolist() # material coefficients mat = Material(pb.material) if pb.solve['kind'] is 'GaNi': A = mat.get_A_GaNi(pb.solve['N'], primaldual) elif pb.solve['kind'] is 'Ga': A = mat.get_A_Ga(Nbar=Nbar, primaldual=primaldual) if primaldual is 'primal': GN = G1N else: GN = G2N Afun = Operator(name='FiGFA', mat=[[GN, A]]) D = int(pb.dim*(pb.dim+1)/2) for iL in range(D): # iteration over unitary loads E = np.zeros(D) E[iL] = 1 print('macroscopic load E = ' + str(E)) EN = Tensor(name='EN', N=Nbar, shape=(D,), Fourier=False) EN.set_mean(E) # initial approximation for solvers x0 = EN.zeros_like(name='x0') B = Afun(-EN) # RHS if not hasattr(pb.solver, 'callback'): cb = CallBack(A=Afun, B=B) elif pb.solver['callback'] == 'detailed': cb = CallBack_GA(A=Afun, B=B, EN=EN, A_Ga=A, GN=GN) else: raise NotImplementedError("The solver callback (%s) is not \ implemented" % (pb.solver['callback'])) print('solver : %s' % pb.solver['kind']) X, info = linear_solver(solver=pb.solver['kind'], Afun=Afun, B=B, x0=x0, par=pb.solver, callback=cb) solutions[iL] = add_macro2minimizer(X, E) results[iL] = {'cb': cb, 'info': info} print(cb) tim.measure() # POSTPROCESSING del Afun, B, E, EN, GN, X postprocess(pb, A, mat, solutions, results, primaldual)
def scalar(N, Y, NyqNul=True, tensor=True, fft_form=fft_form_default): """ Assembly of discrete kernels in Fourier space for scalar elliptic problems. Parameters ---------- N : numpy.ndarray no. of discretization points Y : numpy.ndarray size of periodic unit cell Returns ------- G1l : numpy.ndarray discrete kernel in Fourier space; provides projection on curl-free fields with zero mean G2l : numpy.ndarray discrete kernel in Fourier space; provides projection on divergence-free fields with zero mean """ if fft_form in ['r']: fft_form_r = True fft_form = 0 else: fft_form_r = False d = np.size(N) N = np.array(N, dtype=np.int) if NyqNul: Nred = get_Nodd(N) else: Nred = N xi = Grid.get_xil(Nred, Y, fft_form=fft_form) xi2 = [] for m in np.arange(d): xi2.append(xi[m]**2) G0l = np.zeros(np.hstack([d, d, Nred])) G1l = np.zeros(np.hstack([d, d, Nred])) G2l = np.zeros(np.hstack([d, d, Nred])) num = np.zeros(np.hstack([d, d, Nred])) denom = np.zeros(Nred) ind_center = mean_index(Nred, fft_form=fft_form) for m in np.arange(d): # diagonal components Nshape = np.ones(d, dtype=np.int) Nshape[m] = Nred[m] Nrep = np.copy(Nred) Nrep[m] = 1 a = np.reshape(xi2[m], Nshape) num[m][m] = np.tile(a, Nrep) # numerator denom = denom + num[m][m] G0l[m, m][ind_center] = 1 for m in np.arange(d): # upper diagonal components for n in np.arange(m + 1, d): NshapeM = np.ones(d, dtype=np.int) NshapeM[m] = Nred[m] NrepM = np.copy(Nred) NrepM[m] = 1 NshapeN = np.ones(d, dtype=np.int) NshapeN[n] = Nred[n] NrepN = np.copy(Nred) NrepN[n] = 1 num[m][n] = np.tile(np.reshape(xi[m], NshapeM), NrepM) \ * np.tile(np.reshape(xi[n], NshapeN), NrepN) # avoiding a division by zero denom[ind_center] = 1 # calculation of projections for m in np.arange(d): for n in np.arange(m, d): G1l[m][n] = num[m][n] / denom G2l[m][n] = (m == n) * np.ones(Nred) - G1l[m][n] G2l[m][n][ind_center] = 0 # symmetrization for m in np.arange(1, d): for n in np.arange(m): G1l[m][n] = G1l[n][m] G2l[m][n] = G2l[n][m] if tensor: G0l = Tensor(name='hG0', val=G0l, order=2, N=N, multype=21, Fourier=True, fft_form=fft_form) G1l = Tensor(name='hG1', val=G1l, order=2, N=N, multype=21, Fourier=True, fft_form=fft_form) G2l = Tensor(name='hG2', val=G2l, order=2, N=N, multype=21, Fourier=True, fft_form=fft_form) else: G0l = Matrix(name='hG0', val=G0l, Fourier=True) G1l = Matrix(name='hG1', val=G1l, Fourier=True) G2l = Matrix(name='hG2', val=G2l, Fourier=True) if NyqNul: G0l = G0l.enlarge(N) G1l = G1l.enlarge(N) G2l = G2l.enlarge(N) if fft_form_r: for tensor in [G0l, G1l, G2l]: tensor.set_fft_form(fft_form='r') tensor.val /= np.prod(tensor.N) return G0l, G1l, G2l
def test_compatibility(self): print('\nChecking compatibility...') for dim, fft_form in itertools.product([3], fft_forms): N = 5 * np.ones(dim, dtype=np.int) F = DFT(inverse=False, N=N, fft_form=fft_form) iF = DFT(inverse=True, N=N, fft_form=fft_form) # scalar problem _, G1l, G2l = scalar(N, Y=np.ones(dim), fft_form=fft_form) P1 = Operator(name='P1', mat=[[iF, G1l, F]]) P2 = Operator(name='P2', mat=[[iF, G2l, F]]) u = Tensor(name='u', shape=(1, ), N=N, Fourier=False, fft_form=fft_form) u.randomize() grad_u = grad(u) self.assertAlmostEqual(0, (P1(grad_u) - grad_u).norm(), delta=1e-13) self.assertAlmostEqual(0, P2(grad_u).norm(), delta=1e-13) e = P1( Tensor(name='u', shape=(dim, ), N=N, Fourier=False, fft_form=fft_form).randomize()) e2 = grad(potential(e)) self.assertAlmostEqual(0, (e - e2).norm(), delta=1e-13) # vectorial problem hG = elasticity_large_deformation(N=N, Y=np.ones(dim), fft_form=fft_form) P1 = Operator(name='P', mat=[[iF, hG, F]]) u = Tensor(name='u', shape=(dim, ), N=N, Fourier=False, fft_form=fft_form) u.randomize() grad_u = grad(u) val = (P1(grad_u) - grad_u).norm() self.assertAlmostEqual(0, val, delta=1e-13) e = Tensor(name='F', shape=(dim, dim), N=N, Fourier=False, fft_form=fft_form) e = P1(e.randomize()) e2 = grad(potential(e)) self.assertAlmostEqual(0, (e - e2).norm(), delta=1e-13) # transpose P1TT = P1.transpose().transpose() self.assertTrue(P1(grad_u) == P1TT(grad_u)) self.assertTrue(hG == (hG.transpose_left().transpose_left())) self.assertTrue(hG == (hG.transpose_right().transpose_right())) # vectorial problem - symetric gradient hG = elasticity_small_strain(N=N, Y=np.ones(dim), fft_form=fft_form) P1 = Operator(name='P', mat=[[iF, hG, F]]) u = Tensor(name='u', shape=(dim, ), N=N, Fourier=False, fft_form=fft_form) u.randomize() grad_u = symgrad(u) val = (P1(grad_u) - grad_u).norm() self.assertAlmostEqual(0, val, delta=1e-13) e = Tensor(name='strain', shape=(dim, dim), N=N, Fourier=False, fft_form=fft_form) e = P1(e.randomize()) e2 = symgrad(potential(e, small_strain=True)) self.assertAlmostEqual(0, (e - e2).norm(), delta=1e-13) # means Fu = F(u) E = np.random.random(u.shape) u.set_mean(E) self.assertAlmostEqual(0, norm(u.mean() - E), delta=1e-13) Fu.set_mean(E) self.assertAlmostEqual(0, norm(Fu.mean() - E), delta=1e-13) # __repr__ prt.disable() print(P1) print(u) prt.enable() self.assertAlmostEqual(0, (P1 == P1.transpose()), delta=1e-13) print('...ok')
def test_even(self): print('\nChecking Tensors with even grid points...') for dim, n, fft_form in itertools.product([2, 3], [4, 5], fft_forms): msg = 'Tensors with: dim={}, n={}, fft_form={}'.format( dim, n, fft_form) N = dim * (n, ) M = tuple(2 * np.array(N)) u = Tensor(name='test', shape=(), N=N, Fourier=False, fft_form=fft_form) u.randomize() Fu = u.fourier(copy=True) FuM = Fu.project(M) uM = FuM.fourier() if n % 2 == 0: self.assertGreaterEqual(u.norm(), FuM.norm(), msg=msg) self.assertGreaterEqual(u.norm(componentwise=True), FuM.norm(componentwise=True), msg=msg) self.assertGreaterEqual(u.norm(), uM.norm(), msg=msg) self.assertGreaterEqual(u.norm(componentwise=True), uM.norm(componentwise=True), msg=msg) else: self.assertAlmostEqual(u.norm(), FuM.norm(), msg=msg) self.assertAlmostEqual(u.norm(componentwise=True), FuM.norm(componentwise=True), msg=msg) self.assertAlmostEqual(u.norm(), uM.norm(), msg=msg) self.assertAlmostEqual(u.norm(componentwise=True), uM.norm(componentwise=True), msg=msg) self.assertAlmostEqual(0, u.mean() - FuM.mean(), msg=msg) self.assertAlmostEqual(u.mean(), uM.mean(), msg=msg) # testing that that interpolation on double grid have exactly the same values slc = tuple(u.order * [ slice(None), ] + [slice(0, M[i], 2) for i in range(dim)]) self.assertAlmostEqual(0, np.linalg.norm(u.val - uM.val[slc]), msg=msg) print('...ok')
def elasticity(N, Y, NyqNul=True, tensor=True, fft_form=fft_form_default): """ Projection matrix on a space of admissible strain fields INPUT = N : ndarray of e.g. stiffness coefficients d : dimension; d = 2 D : dimension in engineering notation; D = 3 Y : the size of periodic unit cell OUTPUT = G1h,G1s,G2h,G2s : projection matrices of size DxDxN """ if fft_form in ['r']: fft_form_r=True fft_form=0 else: fft_form_r=False xi = Grid.get_xil(N, Y, fft_form=fft_form) N = np.array(N, dtype=np.int) d = N.size D = int(d*(d+1)/2) if NyqNul: Nred = get_Nodd(N) else: Nred = N xi2 = [] for ii in range(d): xi2.append(xi[ii]**2) num = np.zeros(np.hstack([d, d, Nred])) norm2_xi = np.zeros(Nred) for mm in np.arange(d): # diagonal components Nshape = np.ones(d, dtype=np.int) Nshape[mm] = Nred[mm] Nrep = np.copy(Nred) Nrep[mm] = 1 num[mm][mm] = np.tile(np.reshape(xi2[mm], Nshape), Nrep) # numerator norm2_xi += num[mm][mm] norm4_xi = norm2_xi**2 ind_center = mean_index(Nred, fft_form=fft_form) # avoid division by zero norm2_xi[ind_center] = 1 norm4_xi[ind_center] = 1 for m in np.arange(d): # upper diagonal components for n in np.arange(m+1, d): NshapeM = np.ones(d, dtype=np.int) NshapeM[m] = Nred[m] NrepM = np.copy(Nred) NrepM[m] = 1 NshapeN = np.ones(d, dtype=np.int) NshapeN[n] = Nred[n] NrepN = np.copy(Nred) NrepN[n] = 1 num[m][n] = np.tile(np.reshape(xi[m], NshapeM), NrepM) \ * np.tile(np.reshape(xi[n], NshapeN), NrepN) # G1h = np.zeros([D,D]).tolist() G1h = np.zeros(np.hstack([D, D, Nred])) G1s = np.zeros(np.hstack([D, D, Nred])) IS0 = np.zeros(np.hstack([D, D, Nred])) mean = np.zeros(np.hstack([D, D, Nred])) Lamh = np.zeros(np.hstack([D, D, Nred])) S = np.zeros(np.hstack([D, D, Nred])) W = np.zeros(np.hstack([D, D, Nred])) WT = np.zeros(np.hstack([D, D, Nred])) for m in np.arange(d): S[m][m] = 2*num[m][m]/norm2_xi for n in np.arange(d): G1h[m][n] = num[m][m]*num[n][n]/norm4_xi Lamh[m][n] = np.ones(Nred)/d Lamh[m][n][ind_center] = 0 for m in np.arange(D): IS0[m][m] = np.ones(Nred) IS0[m][m][ind_center] = 0 mean[m][m][ind_center] = 1 if d == 2: S[0][2] = 2**0.5*num[0][1]/norm2_xi S[1][2] = 2**0.5*num[0][1]/norm2_xi S[2][2] = np.ones(Nred) S[2][2][ind_center] = 0 G1h[0][2] = 2**0.5*num[0][0]*num[0][1]/norm4_xi G1h[1][2] = 2**0.5*num[0][1]*num[1][1]/norm4_xi G1h[2][2] = 2*num[0][0]*num[1][1]/norm4_xi for m in np.arange(d): for n in np.arange(d): W[m][n] = num[m][m]/norm2_xi W[2][m] = 2**.5*num[0][1]/norm2_xi elif d == 3: for m in np.arange(d): S[m+3][m+3] = 1 - num[m][m]/norm2_xi S[m+3][m+3][ind_center] = 0 for m in np.arange(d): for n in np.arange(m+1, d): S[m+3][n+3] = num[m][n]/norm2_xi G1h[m+3][n+3] = num[m][m]*num[n][n]/norm4_xi for m in np.arange(d): for n in np.arange(d): ind = sp.setdiff1d(np.arange(d), [n]) S[m][n+3] = (0 == (m == n))*2**.5*num[ind[0]][ind[1]]/norm2_xi G1h[m][n+3] = 2**.5*num[m][m]*num[ind[0]][ind[1]]/norm4_xi W[m][n] = num[m][m]/norm2_xi W[n+3][m] = 2**.5*num[ind[0]][ind[1]]/norm2_xi for m in np.arange(d): for n in np.arange(d): ind_m = sp.setdiff1d(np.arange(d), [m]) ind_n = sp.setdiff1d(np.arange(d), [n]) G1h[m+3][n+3] = 2*num[ind_m[0]][ind_m[1]] \ * num[ind_n[0]][ind_n[1]] / norm4_xi # symmetrization for n in np.arange(D): for m in np.arange(n+1, D): S[m][n] = S[n][m] G1h[m][n] = G1h[n][m] for m in np.arange(D): for n in np.arange(D): G1s[m][n] = S[m][n] - 2*G1h[m][n] WT[m][n] = W[n][m] G2h = 1./(d-1)*(d*Lamh + G1h - W - WT) G2s = IS0 - G1h - G1s - G2h if tensor: G0 = Tensor(name='hG0', val=mean, order=2, N=N, Fourier=True, multype=21, fft_form=fft_form) G1h = Tensor(name='hG1h', val=G1h, order=2, N=N, Fourier=True, multype=21, fft_form=fft_form) G1s = Tensor(name='hG1s', val=G1s, order=2, N=N, Fourier=True, multype=21, fft_form=fft_form) G2h = Tensor(name='hG2h', val=G2h, order=2, N=N, Fourier=True, multype=21, fft_form=fft_form) G2s = Tensor(name='hG2s', val=G2s, order=2, N=N, Fourier=True, multype=21, fft_form=fft_form) else: G0 = Matrix(name='hG0', val=mean, Fourier=True) G1h = Matrix(name='hG1h', val=G1h, Fourier=True) G1s = Matrix(name='hG1s', val=G1s, Fourier=True) G2h = Matrix(name='hG2h', val=G2h, Fourier=True) G2s = Matrix(name='hG2s', val=G2s, Fourier=True) if NyqNul: G0 = G0.enlarge(N) G1h = G1h.enlarge(N) G1s = G1s.enlarge(N) G2h = G2h.enlarge(N) G2s = G2s.enlarge(N) if fft_form_r: for tensor in [G0, G1h, G1s, G2h, G2s]: tensor.set_fft_form(fft_form='r') tensor.val=1./np.prod(tensor.N)*tensor.val return G0, G1h, G1s, G2h, G2s
def scalar(N, Y, NyqNul=True, tensor=True, fft_form=fft_form_default): """ Assembly of discrete kernels in Fourier space for scalar elliptic problems. Parameters ---------- N : numpy.ndarray no. of discretization points Y : numpy.ndarray size of periodic unit cell Returns ------- G1l : numpy.ndarray discrete kernel in Fourier space; provides projection on curl-free fields with zero mean G2l : numpy.ndarray discrete kernel in Fourier space; provides projection on divergence-free fields with zero mean """ if fft_form in ['r']: fft_form_r=True fft_form=0 else: fft_form_r=False d = np.size(N) N = np.array(N, dtype=np.int) if NyqNul: Nred = get_Nodd(N) else: Nred = N xi = Grid.get_xil(Nred, Y, fft_form=fft_form) xi2 = [] for m in np.arange(d): xi2.append(xi[m]**2) G0l = np.zeros(np.hstack([d, d, Nred])) G1l = np.zeros(np.hstack([d, d, Nred])) G2l = np.zeros(np.hstack([d, d, Nred])) num = np.zeros(np.hstack([d, d, Nred])) denom = np.zeros(Nred) ind_center = mean_index(Nred, fft_form=fft_form) for m in np.arange(d): # diagonal components Nshape = np.ones(d, dtype=np.int) Nshape[m] = Nred[m] Nrep = np.copy(Nred) Nrep[m] = 1 a = np.reshape(xi2[m], Nshape) num[m][m] = np.tile(a, Nrep) # numerator denom = denom + num[m][m] G0l[m, m][ind_center] = 1 for m in np.arange(d): # upper diagonal components for n in np.arange(m+1, d): NshapeM = np.ones(d, dtype=np.int) NshapeM[m] = Nred[m] NrepM = np.copy(Nred) NrepM[m] = 1 NshapeN = np.ones(d, dtype=np.int) NshapeN[n] = Nred[n] NrepN = np.copy(Nred) NrepN[n] = 1 num[m][n] = np.tile(np.reshape(xi[m], NshapeM), NrepM) \ * np.tile(np.reshape(xi[n], NshapeN), NrepN) # avoiding a division by zero denom[ind_center] = 1 # calculation of projections for m in np.arange(d): for n in np.arange(m, d): G1l[m][n] = num[m][n]/denom G2l[m][n] = (m == n)*np.ones(Nred) - G1l[m][n] G2l[m][n][ind_center] = 0 # symmetrization for m in np.arange(1, d): for n in np.arange(m): G1l[m][n] = G1l[n][m] G2l[m][n] = G2l[n][m] if tensor: G0l = Tensor(name='hG0', val=G0l, order=2, N=N, multype=21, Fourier=True, fft_form=fft_form) G1l = Tensor(name='hG1', val=G1l, order=2, N=N, multype=21, Fourier=True, fft_form=fft_form) G2l = Tensor(name='hG2', val=G2l, order=2, N=N, multype=21, Fourier=True, fft_form=fft_form) else: G0l = Matrix(name='hG0', val=G0l, Fourier=True) G1l = Matrix(name='hG1', val=G1l, Fourier=True) G2l = Matrix(name='hG2', val=G2l, Fourier=True) if NyqNul: G0l = G0l.enlarge(N) G1l = G1l.enlarge(N) G2l = G2l.enlarge(N) if fft_form_r: for tensor in [G0l, G1l, G2l]: tensor.set_fft_form(fft_form='r') tensor.val/=np.prod(tensor.N) return G0l, G1l, G2l
'postprocess': [{'kind': 'GaNi'}], 'solver': {'kind': 'CG', 'tol': 1e-8, 'maxiter': 1e3}} print(""" The material properties for FFT-based homogenization are stored at grid points and are represented with class 'Matrix', which has the same or similar attributes and methods as a VecTri class from 'tutorial_01.py'. In order to show its application, we create a material coefficients composed of random values, symmetrize them, and sum them with a multiplication of identity to obtain positive definite matrix, i.e. A =""") from ffthompy.tensors import Tensor D = int(dim*(dim+1)/2) A = Tensor(N=N, shape=(D,D), Fourier=False, multype=21) A.randomize() A = A + A.transpose() # symmetrization # adding a multiplication of identity matrix: I = Tensor(N=N, shape=(D,D), Fourier=False, multype=21) I.identity() A += 3*I A.name='material' print(A) print(""" The values of material coefficients are stored in 'self.val', which is 'numpy.ndarray' object of shape = (D, D) + tuple(N) here D=d*(d+1)/2 is a number of components of elasticity matrix in engineering notation, so A.val.shape =""")
from ffthompy.tensors import Tensor, DFT print(""" The work with trigonometric polynomials is shown for""") d = 2 N = 5*np.ones(d, dtype=np.int32) print('dimension d =', d) print('number of grid points N =', N) print('which is implemented as a numpy.ndarray.') print(""" Particularly, the vector-valued trigonometric polynomial is created as an instance 'xN' of class 'Tensor' and the random values are assigned. """) xN = Tensor(name='trigpol_rand', shape=(d,), N=N) xN.randomize() print(""" Basic properties of a trigonometric polynomials can be printed with a norm corresponding to L2 norm of trigonometric polynomial, i.e. xN =""") print(xN) print(""" The values of trigonometric polynomials are stored in atribute val of type numpy.ndarray with shape = (self.d,) + tuple(self.N), i.e. xN.val.shape =""") print(xN.val.shape) print("xN.val = xN[:] =") print(xN.val)
A = mat.get_A_Ga(Nbar=Nbar, primaldual=pb['solve']['primaldual'][0]) # projections in Fourier space _, hG1N, _ = proj.scalar(pb['solve']['N'], pb['material']['Y'], NyqNul=True, tensor=True) # increasing the projection with zeros to comply with a projection # on double grid, see Definition 24 in IJNME2016 hG1N = hG1N.enlarge(Nbar) FN = DFT(name='FN', inverse=False, N=Nbar) # discrete Fourier transform (DFT) FiN = DFT(name='FiN', inverse=True, N=Nbar) # inverse DFT G1N = Operator(name='G1', mat=[[FiN, hG1N, FN]]) # projection in original space Afun = Operator(name='FiGFA', mat=[[G1N, A]]) # lin. operator for a linear system E = np.zeros(dim); E[0] = 1 # macroscopic load EN = Tensor(name='EN', N=Nbar, shape=(dim,), Fourier=False) # constant trig. pol. EN.set_mean(E) x0 = Tensor(N=Nbar, shape=(dim,), Fourier=False) # initial approximation to solvers B = Afun(-EN) # right-hand side of linear system X, info = linear_solver(solver='CG', Afun=Afun, B=B, x0=x0, par=pb['solver'], callback=None) print('homogenised properties (component 11) =', A(X + EN)*(X + EN)) if __name__ == "__main__": ## plotting of local fields ################## X.plot(ind=0, N=N) print('END')
def test_Fourier(self): print('\nChecking Fourier functions ...') for opt in [0, 'c']: a = SparseTensor(kind='cano', val=self.T2d, fft_form=opt) T = Tensor(val=self.T2d, order=0, N=self.T2d.shape, Fourier=False, fft_form=opt) self.assertAlmostEqual( norm(a.fourier().full(fft_form=opt).val - T.fourier(copy=True).val), 0) self.assertEqual( norm(a.fourier().fourier(real_output=True).full().val.imag), 0) a = SparseTensor(kind='tucker', val=self.T3d, fft_form=opt) T = Tensor(val=self.T3d, order=0, N=self.T3d.shape, Fourier=False, fft_form=opt) self.assertAlmostEqual( norm(a.fourier().full(fft_form=opt) - T.fourier(copy=True)), 0) self.assertEqual( norm(a.fourier().fourier(real_output=True).full().val.imag), 0) a = SparseTensor(kind='tt', val=self.T3d, fft_form=opt) T = Tensor(val=self.T3d, order=0, N=self.T3d.shape, Fourier=False, fft_form=opt) self.assertAlmostEqual( norm(a.fourier().full(fft_form=opt) - T.fourier(copy=True).val), 0) self.assertEqual( norm(a.fourier().fourier(real_output=True).full().val.imag), 0) # checking shifting fft_forms sparse_opt = 'sr' for full_opt in [0, 'c']: a = SparseTensor(kind='cano', val=self.T2d, fft_form=sparse_opt) T = Tensor(val=self.T2d, order=0, N=self.T2d.shape, Fourier=False, fft_form=full_opt) self.assertAlmostEqual( norm(a.fourier().full(fft_form=full_opt) - T.fourier(copy=True)), 0) self.assertAlmostEqual((a.fourier().set_fft_form(full_opt) - a.set_fft_form(full_opt).fourier()).norm(), 0) a = SparseTensor(kind='tucker', val=self.T3d, fft_form=sparse_opt) T = Tensor(val=self.T3d, order=0, N=self.T3d.shape, Fourier=False, fft_form=full_opt) self.assertAlmostEqual( norm(a.fourier().full(fft_form=full_opt) - T.fourier(copy=True)), 0) self.assertAlmostEqual((a.fourier().set_fft_form(full_opt) - a.set_fft_form(full_opt).fourier()).norm(), 0) a = SparseTensor(kind='tt', val=self.T3d, fft_form=sparse_opt) T = Tensor(val=self.T3d, order=0, N=self.T3d.shape, Fourier=False, fft_form=full_opt) self.assertAlmostEqual( norm(a.fourier().full(fft_form=full_opt) - T.fourier(copy=True)), 0) self.assertAlmostEqual((a.fourier().set_fft_form(full_opt) - a.set_fft_form(full_opt).fourier()).norm(), 0) print('...ok')
from ffthompy.tensors import Tensor, DFT print(""" The work with trigonometric polynomials is shown for""") d = 2 N = 5 * np.ones(d, dtype=np.int32) print('dimension d =', d) print('number of grid points N =', N) print('which is implemented as a numpy.ndarray.') print(""" Particularly, the vector-valued trigonometric polynomial is created as an instance 'xN' of class 'Tensor' and the random values are assigned. """) xN = Tensor(name='trigpol_rand', shape=(d, ), N=N) xN.randomize() print(""" Basic properties of a trigonometric polynomials can be printed with a norm corresponding to L2 norm of trigonometric polynomial, i.e. xN =""") print(xN) print(""" The values of trigonometric polynomials are stored in atribute val of type numpy.ndarray with shape = (self.d,) + tuple(self.N), i.e. xN.val.shape =""") print(xN.val.shape) print("xN.val = xN[:] =") print(xN.val)
def test_operators(self): print('\nChecking operators...') for dim, fft_form in itertools.product([2, 3], fft_forms): N = 5 * np.ones(dim, dtype=np.int) F = DFT(N=N, inverse=False, fft_form=fft_form) iF = DFT(N=N, inverse=True, fft_form=fft_form) # Fourier transform prt.disable() print(F) # checking representation prt.enable() u = Tensor(name='u', shape=(), N=N, Fourier=False, fft_form=fft_form).randomize() Fu = F(u) u2 = iF(Fu) self.assertAlmostEqual(0, (u == u2)[1], delta=1e-13, msg='Fourier transform') fft_formsC = copy(fft_forms) fft_formsC.remove(fft_form) for fft_formc in fft_formsC: FuC = Fu.set_fft_form(fft_formc, copy=True) Fu2 = FuC.set_fft_form(fft_form, copy=True) msg = 'Tensor.set_fft_form()' self.assertAlmostEqual(0, Fu.norm() - FuC.norm(), delta=1e-13, msg=msg) self.assertAlmostEqual(0, norm(Fu.mean() - FuC.mean()), delta=1e-13, msg=msg) self.assertAlmostEqual(0, (Fu == Fu2)[1], delta=1e-13, msg=msg) # scalar problem u = Tensor(name='u', shape=(1, ), N=N, Fourier=False, fft_form=fft_form).randomize() u.val -= np.mean(u.val) Fu = F(u) Fu2 = potential(grad(Fu)) self.assertAlmostEqual(0, (Fu == Fu2)[1], delta=1e-13, msg='scalar problem, Fourier=True') u2 = potential(grad(u)) self.assertAlmostEqual(0, (u == u2)[1], delta=1e-13, msg='scalar problem, Fourier=False') hG, hD = grad_div_tensor(N, fft_form=fft_form) self.assertAlmostEqual(0, (hD(hG(Fu)) == div(grad(Fu)))[1], delta=1e-13, msg='scalar problem, Fourier=True') # vectorial problem u = Tensor(name='u', shape=(dim, ), N=N, Fourier=False, fft_form=fft_form) u.randomize() u.add_mean(-u.mean()) Fu = F(u) Fu2 = potential(grad(Fu)) self.assertAlmostEqual(0, (Fu == Fu2)[1], delta=1e-13, msg='vectorial problem, Fourier=True') u2 = potential(grad(u)) self.assertAlmostEqual(0, (u == u2)[1], delta=1e-13, msg='vectorial problem, Fourier=False') # 'vectorial problem - symetric gradient Fu2 = potential(symgrad(Fu), small_strain=True) self.assertAlmostEqual(0, (Fu == Fu2)[1], delta=1e-13, msg='vectorial - sym, Fourier=True') u2 = potential(symgrad(u), small_strain=True) self.assertAlmostEqual(0, (u == u2)[1], delta=1e-13, msg='vectorial - sym, Fourier=False') # matrix version of DFT u = Tensor(name='u', shape=(1, ), N=N, Fourier=False, fft_form='c').randomize() F = DFT(N=N, inverse=False, fft_form='c') Fu = F(u) dft = F.matrix(shape=u.shape) Fu2 = dft.dot(u.val.ravel()) self.assertAlmostEqual(0, norm(Fu.val.ravel() - Fu2), delta=1e-13) print('...ok')
def test_compatibility(self): print('\nChecking compatibility...') for dim, fft_form in itertools.product([3], fft_forms): N=5*np.ones(dim, dtype=np.int) F=DFT(inverse=False, N=N, fft_form=fft_form) iF=DFT(inverse=True, N=N, fft_form=fft_form) # scalar problem _, G1l, G2l=scalar(N, Y=np.ones(dim), fft_form=fft_form) P1=Operator(name='P1', mat=[[iF, G1l, F]]) P2=Operator(name='P2', mat=[[iF, G2l, F]]) u=Tensor(name='u', shape=(1,), N=N, Fourier=False, fft_form=fft_form) u.randomize() grad_u=grad(u) self.assertAlmostEqual(0, (P1(grad_u)-grad_u).norm(), delta=1e-13) self.assertAlmostEqual(0, P2(grad_u).norm(), delta=1e-13) e=P1(Tensor(name='u', shape=(dim,), N=N, Fourier=False, fft_form=fft_form).randomize()) e2=grad(potential(e)) self.assertAlmostEqual(0, (e-e2).norm(), delta=1e-13) # vectorial problem hG=elasticity_large_deformation(N=N, Y=np.ones(dim), fft_form=fft_form) P1=Operator(name='P', mat=[[iF, hG, F]]) u=Tensor(name='u', shape=(dim,), N=N, Fourier=False, fft_form=fft_form) u.randomize() grad_u=grad(u) val=(P1(grad_u)-grad_u).norm() self.assertAlmostEqual(0, val, delta=1e-13) e=Tensor(name='F', shape=(dim, dim), N=N, Fourier=False, fft_form=fft_form) e=P1(e.randomize()) e2=grad(potential(e)) self.assertAlmostEqual(0, (e-e2).norm(), delta=1e-13) # transpose P1TT=P1.transpose().transpose() self.assertTrue(P1(grad_u)==P1TT(grad_u)) self.assertTrue(hG==(hG.transpose_left().transpose_left())) self.assertTrue(hG==(hG.transpose_right().transpose_right())) # vectorial problem - symetric gradient hG=elasticity_small_strain(N=N, Y=np.ones(dim), fft_form=fft_form) P1=Operator(name='P', mat=[[iF, hG, F]]) u=Tensor(name='u', shape=(dim,), N=N, Fourier=False, fft_form=fft_form) u.randomize() grad_u=symgrad(u) val=(P1(grad_u)-grad_u).norm() self.assertAlmostEqual(0, val, delta=1e-13) e=Tensor(name='strain', shape=(dim, dim), N=N, Fourier=False, fft_form=fft_form) e=P1(e.randomize()) e2=symgrad(potential(e, small_strain=True)) self.assertAlmostEqual(0, (e-e2).norm(), delta=1e-13) # means Fu=F(u) E=np.random.random(u.shape) u.set_mean(E) self.assertAlmostEqual(0, norm(u.mean()-E), delta=1e-13) Fu.set_mean(E) self.assertAlmostEqual(0, norm(Fu.mean()-E), delta=1e-13) # __repr__ prt.disable() print(P1) print(u) prt.enable() self.assertAlmostEqual(0, (P1==P1.transpose()), delta=1e-13) print('...ok')
def elasticity(problem): """ Homogenization of linear elasticity. Parameters ---------- problem : object """ print(' ') pb = problem print(pb) # Fourier projections _, hG1hN, hG1sN, hG2hN, hG2sN = proj.elasticity(pb.solve['N'], pb.Y, NyqNul=True) del _ if pb.solve['kind'] is 'GaNi': Nbar = pb.solve['N'] elif pb.solve['kind'] is 'Ga': Nbar = 2 * pb.solve['N'] - 1 hG1hN = hG1hN.enlarge(Nbar) hG1sN = hG1sN.enlarge(Nbar) hG2hN = hG2hN.enlarge(Nbar) hG2sN = hG2sN.enlarge(Nbar) FN = DFT(name='FN', inverse=False, N=Nbar) FiN = DFT(name='FiN', inverse=True, N=Nbar) G1N = Operator(name='G1', mat=[[FiN, hG1hN + hG1sN, FN]]) G2N = Operator(name='G2', mat=[[FiN, hG2hN + hG2sN, FN]]) for primaldual in pb.solve['primaldual']: tim = Timer(name='primal-dual') print(('\nproblem: ' + primaldual)) solutions = np.zeros(pb.shape).tolist() results = np.zeros(pb.shape).tolist() # material coefficients mat = Material(pb.material) if pb.solve['kind'] is 'GaNi': A = mat.get_A_GaNi(pb.solve['N'], primaldual) elif pb.solve['kind'] is 'Ga': A = mat.get_A_Ga(Nbar=Nbar, primaldual=primaldual) if primaldual is 'primal': GN = G1N else: GN = G2N Afun = Operator(name='FiGFA', mat=[[GN, A]]) D = int(pb.dim * (pb.dim + 1) / 2) for iL in range(D): # iteration over unitary loads E = np.zeros(D) E[iL] = 1 print(('macroscopic load E = ' + str(E))) EN = Tensor(name='EN', N=Nbar, shape=(D, ), Fourier=False) EN.set_mean(E) # initial approximation for solvers x0 = EN.zeros_like(name='x0') B = Afun(-EN) # RHS if not hasattr(pb.solver, 'callback'): cb = CallBack(A=Afun, B=B) elif pb.solver['callback'] == 'detailed': cb = CallBack_GA(A=Afun, B=B, EN=EN, A_Ga=A, GN=GN) else: raise NotImplementedError("The solver callback (%s) is not \ implemented" % (pb.solver['callback'])) print(('solver : %s' % pb.solver['kind'])) X, info = linear_solver(solver=pb.solver['kind'], Afun=Afun, B=B, x0=x0, par=pb.solver, callback=cb) solutions[iL] = add_macro2minimizer(X, E) results[iL] = {'cb': cb, 'info': info} print(cb) tim.measure() # POSTPROCESSING del Afun, B, E, EN, GN, X postprocess(pb, A, mat, solutions, results, primaldual)
def test_operators(self): print('\nChecking operators...') for dim, fft_form in itertools.product([2, 3], fft_forms): N=5*np.ones(dim, dtype=np.int) F=DFT(N=N, inverse=False, fft_form=fft_form) iF=DFT(N=N, inverse=True, fft_form=fft_form) # Fourier transform prt.disable() print(F) # checking representation prt.enable() u=Tensor(name='u', shape=(), N=N, Fourier=False, fft_form=fft_form).randomize() Fu=F(u) u2=iF(Fu) self.assertAlmostEqual(0, (u==u2)[1], delta=1e-13, msg='Fourier transform') fft_formsC=copy(fft_forms) fft_formsC.remove(fft_form) for fft_formc in fft_formsC: FuC=Fu.set_fft_form(fft_formc, copy=True) Fu2=FuC.set_fft_form(fft_form, copy=True) msg='Tensor.set_fft_form()' self.assertAlmostEqual(0, Fu.norm()-FuC.norm(), delta=1e-13, msg=msg) self.assertAlmostEqual(0, norm(Fu.mean()-FuC.mean()), delta=1e-13, msg=msg) self.assertAlmostEqual(0, (Fu==Fu2)[1], delta=1e-13, msg=msg) # scalar problem u=Tensor(name='u', shape=(1,), N=N, Fourier=False, fft_form=fft_form).randomize() u.val-=np.mean(u.val) Fu=F(u) Fu2=potential(grad(Fu)) self.assertAlmostEqual(0, (Fu==Fu2)[1], delta=1e-13, msg='scalar problem, Fourier=True') u2=potential(grad(u)) self.assertAlmostEqual(0, (u==u2)[1], delta=1e-13, msg='scalar problem, Fourier=False') hG, hD=grad_div_tensor(N, fft_form=fft_form) self.assertAlmostEqual(0, (hD(hG(Fu))==div(grad(Fu)))[1], delta=1e-13, msg='scalar problem, Fourier=True') # vectorial problem u=Tensor(name='u', shape=(dim,), N=N, Fourier=False, fft_form=fft_form) u.randomize() u.add_mean(-u.mean()) Fu=F(u) Fu2=potential(grad(Fu)) self.assertAlmostEqual(0, (Fu==Fu2)[1], delta=1e-13, msg='vectorial problem, Fourier=True') u2=potential(grad(u)) self.assertAlmostEqual(0, (u==u2)[1], delta=1e-13, msg='vectorial problem, Fourier=False') # 'vectorial problem - symetric gradient Fu2=potential(symgrad(Fu), small_strain=True) self.assertAlmostEqual(0, (Fu==Fu2)[1], delta=1e-13, msg='vectorial - sym, Fourier=True') u2=potential(symgrad(u), small_strain=True) self.assertAlmostEqual(0, (u==u2)[1], delta=1e-13, msg='vectorial - sym, Fourier=False') # matrix version of DFT u=Tensor(name='u', shape=(1,), N=N, Fourier=False, fft_form='c').randomize() F=DFT(N=N, inverse=False, fft_form='c') Fu=F(u) dft=F.matrix(shape=u.shape) Fu2=dft.dot(u.val.ravel()) self.assertAlmostEqual(0, norm(Fu.val.ravel()-Fu2), delta=1e-13) print('...ok')
'tol': 1e-8, 'maxiter': 1e3 } } print(""" The material properties for FFT-based homogenization are stored at grid points and are represented with class 'Matrix', which has the same or similar attributes and methods as a VecTri class from 'tutorial_01.py'. In order to show its application, we create a material coefficients composed of random values, symmetrize them, and sum them with a multiplication of identity to obtain positive definite matrix, i.e. A =""") from ffthompy.tensors import Tensor D = int(dim * (dim + 1) / 2) A = Tensor(N=N, shape=(D, D), Fourier=False, multype=21) A.randomize() A = A + A.transpose() # symmetrization # adding a multiplication of identity matrix: I = Tensor(N=N, shape=(D, D), Fourier=False, multype=21) I.identity() A += 3 * I A.name = 'material' print(A) print(""" The values of material coefficients are stored in 'self.val', which is 'numpy.ndarray' object of shape = (D, D) + tuple(N) here D=d*(d+1)/2 is a number of components of elasticity matrix in engineering notation, so A.val.shape =""")
tensor=True) # increasing the projection with zeros to comply with a projection # on double grid, see Definition 24 in IJNME2016 hG1N = hG1N.enlarge(Nbar) FN = DFT(name='FN', inverse=False, N=Nbar) # discrete Fourier transform (DFT) FiN = DFT(name='FiN', inverse=True, N=Nbar) # inverse DFT G1N = Operator(name='G1', mat=[[FiN, hG1N, FN]]) # projection in original space Afun = Operator(name='FiGFA', mat=[[G1N, A]]) # lin. operator for a linear system E = np.zeros(dim) E[0] = 1 # macroscopic load EN = Tensor(name='EN', N=Nbar, shape=(dim, ), Fourier=False) # constant trig. pol. EN.set_mean(E) x0 = Tensor(N=Nbar, shape=(dim, ), Fourier=False) # initial approximation to solvers B = Afun(-EN) # right-hand side of linear system X, info = linear_solver(solver='CG', Afun=Afun, B=B, x0=x0, par=pb['solver'], callback=None) print('homogenised properties (component 11) =', A(X + EN) * (X + EN))