def main( nelems: 'number of elements' = 20, degree: 'polynomial degree' = 1, timescale: 'time scale (timestep=timescale/nelems)' = .5, tol: 'solver tolerance' = 1e-5, ndims: 'spatial dimension' = 1, endtime: 'end time, 0 for no end time' = 0, withplots: 'create plots' = True, ): # construct mesh domain, geom = mesh.rectilinear([numpy.linspace(0, 1, nelems + 1)] * ndims, periodic=range(ndims)) basis = domain.basis('discont', degree=degree) # construct initial condition (centered gaussian) u = function.exp(-(((geom - .5) * 5)**2).sum(-1)) lhs = domain.project(u, onto=basis, geometry=geom, ischeme='gauss5') # prepare matrix timestep = timescale / nelems At = (1 / timestep) * function.outer(basis) matrix0 = domain.integrate(At, geometry=geom, ischeme='gauss5') # prepare plotting makeplots = MakePlots(domain, geom, video=withplots == 'video') if withplots else lambda *args: None # start time stepping for itime in log.count('timestep'): makeplots(u) if endtime and itime * timestep >= endtime: break rhs = matrix0.matvec(lhs) for ipicard in log.count('picard'): u = basis.dot(lhs) beta = function.repeat(u[_], ndims, axis=0) Am = -function.outer((basis[:, _] * beta).div(geom), basis) dmatrix = domain.integrate(Am, geometry=geom, ischeme='gauss5') alpha = .5 * function.sign(function.mean(beta).dotnorm(geom)) Ai = function.outer( function.jump(basis), (alpha * function.jump(basis[:, _] * beta) - function.mean(basis[:, _] * beta)).dotnorm(geom)) dmatrix += domain.interfaces.integrate(Ai, geometry=geom, ischeme='gauss5') lhs, info = (matrix0 + dmatrix).solve(rhs, lhs0=lhs, tol=tol, restart=999, precon='spilu', info=True) if info.niter == 0: break return rhs, lhs
def __init__(self, refine=1, degree=3, nel=None): if nel is None: nel = int(10 * refine) xpts = np.linspace(0, 2, 2 * nel + 1) ypts = np.linspace(0, 1, nel + 1) domain, geom = mesh.rectilinear([xpts, ypts]) NutilsCase.__init__(self, 'Channel flow', domain, geom, geom) bases = [ domain.basis('spline', degree=(degree, degree - 1)), # vx domain.basis('spline', degree=(degree - 1, degree)), # vy domain.basis('spline', degree=degree - 1), # pressure [0] * 2, # stabilization terms ] basis_lens = [len(b) for b in bases] vxbasis, vybasis, pbasis, __ = fn.chain(bases) vbasis = vxbasis[:, _] * (1, 0) + vybasis[:, _] * (0, 1) self.bases.add('v', vbasis, length=sum(basis_lens[:2])) self.bases.add('p', pbasis, length=basis_lens[2]) self.extra_dofs = 2 self.constrain('v', 'left', 'top', 'bottom') x, y = geom profile = (y * (1 - y))[_] * (1, 0) self.integrals['lift'] = Affine(1, self.project_lift(profile, 'v')) self.integrals['geometry'] = Affine(1, geom) self._exact_solutions = {'v': profile, 'p': 4 - 2 * x} self.integrals['divergence'] = AffineIntegral( -1, fn.outer(vbasis.div(geom), pbasis)) self.integrals['laplacian'] = AffineIntegral( 1, fn.outer(vbasis.grad(geom)).sum((-1, -2))) self.integrals['v-h1s'] = AffineIntegral( 1, fn.outer(vbasis.grad(geom)).sum([-1, -2])) self.integrals['p-l2'] = AffineIntegral(1, fn.outer(pbasis)) self.integrals['convection'] = AffineIntegral( 1, NutilsDelayedIntegrand('w_ia u_jb v_ka,b', 'ijk', 'wuv', x=geom, w=vbasis, u=vbasis, v=vbasis)) points = [(0, (0, 0)), (nel - 1, (0, 1))] eqn = (vbasis.laplace(geom) - pbasis.grad(geom))[:, 0, _] self.integrals['stab-lhs'] = AffineIntegral( 1, collocate(domain, eqn, points, self.ndofs - self.extra_dofs, self.ndofs))
def __init__(self, physical, n, p): """ __init__(self, physical, n, p) Constructor for finite element object :param physical : (width, height) of physical domain :param n : (n1,n2), number of elements in computational domain :param p : (p1,p2), polynomial degree of compuational discretization """ # create splipy object splinesurf = splines.square() * physical splinesurf.reparam((0, physical[0]), (0, physical[1])) splinesurf.raise_order(p[0] - 1, p[1] - 1) splinesurf.refine(n[0] - 1, n[1] - 1) # create nutils mesh domain, geom = mesh.rectilinear(splinesurf.knots()) basis = domain.basis('spline', degree=p) # pre-compute all system matrices derivs = function.outer(basis.grad(geom), basis.grad(geom)) laplace = derivs[:, :, 0] + derivs[:, :, 1] A = domain.integrate(laplace, geometry=geom, ischeme='gauss3') M = domain.integrate(function.outer(basis, basis), geometry=geom, ischeme='gauss3') # print(M.toarray()) # pre-compute evaluation matrices self.Nu = splinesurf.bases[0].evaluate( np.linspace(splinesurf.start('u'), splinesurf.end('u'), physical[0])) self.Nv = splinesurf.bases[1].evaluate( np.linspace(splinesurf.start('v'), splinesurf.end('v'), physical[1])) # store all nutils object as class variables self.splinesurf = splinesurf self.domain = domain self.geom = geom self.basis = basis self.A = A.toscipy() * 1e2 self.M = M.toscipy() self.u = np.matrix(np.zeros((A.shape[0], 1))) self.b = np.matrix(np.zeros((A.shape[0], 1))) self.n = np.array(n) self.p = np.array(p) self.physical = np.array(physical) self.time = { 'add_smoke': 0, 'get_img': 0, 'stop_smoke': 0, 'diffuse': 0, 'solve': 0 }
class Mass(MuCallable): _ident_ = 'Mass' def __init__(self, n, basisname, *deps, scale=1): super().__init__((n, n), deps, scale=scale) self.basisname = basisname def write(self, group): super().write(group) util.to_dataset(self.basisname, group, 'basisname') def _read(self, group): super()._read(group) self.basisname = util.from_dataset(group['basisname']) def evaluate(self, case, mu, cont): geom = case['geometry'](mu) basis = case.basis(self.basisname, mu) itg = fn.outer(basis) if basis.ndim > 1: itg = itg.sum([-1]) itg = util.contract(itg, cont) with matrix.Scipy(): return unwrap(case.domain.integrate(itg * fn.J(geom), ischeme='gauss9'))
def setUp(self): super().setUp() domain, geom = mesh.rectilinear([numpy.linspace(0, 1, 9)] * 2) ubasis = domain.basis('std', degree=2) if self.vector: u = ubasis.vector(2).dot( function.Argument('dofs', [len(ubasis) * 2])) else: u = (ubasis[:, numpy.newaxis] * function.Argument('dofs', [len(ubasis), 2])).sum(0) Geom = geom * [1.1, 1] + u self.cons = solver.optimize('dofs', domain.boundary['left,right'].integral( (u**2).sum(0), degree=4), droptol=1e-15) self.boolcons = ~numpy.isnan(self.cons) strain = .5 * (function.outer(Geom.grad(geom), axis=1).sum(0) - function.eye(2)) self.energy = domain.integral( ((strain**2).sum([0, 1]) + 20 * (function.determinant(Geom.grad(geom)) - 1)**2) * function.J(geom), degree=6) self.residual = self.energy.derivative('dofs') self.tol = 1e-10
def test_nonlinear_diagonalshift(self): nelems = 10 domain, geom = mesh.rectilinear([nelems, 1]) geom *= [2 * numpy.pi / nelems, 1] ubasis = domain.basis('spline', degree=2).vector(2) u = ubasis.dot(function.Argument('dofs', [len(ubasis)])) Geom = [.5 * geom[0], geom[1] + function.cos(geom[0]) ] + u # compress by 50% and buckle cons = solver.minimize('dofs', domain.boundary['left,right'].integral( (u**2).sum(0), degree=4), droptol=1e-15).solve() strain = .5 * (function.outer(Geom.grad(geom), axis=1).sum(0) - function.eye(2)) energy = domain.integral( ((strain**2).sum([0, 1]) + 150 * (function.determinant(Geom.grad(geom)) - 1)**2) * function.J(geom), degree=6) nshift = 0 for iiter, (lhs, info) in enumerate( solver.minimize('dofs', energy, constrain=cons)): self.assertLess(iiter, 38) if info.shift: nshift += 1 if info.resnorm < self.tol: break self.assertEqual(nshift, 9)
def poisson_patch(bottom, right, top, left): from nutils import version if version != '3.0': raise ImportError('Outdated nutils version detected, only v3.0 supported. Upgrade by \"pip install --upgrade nutils\"') from nutils import mesh, function as fn from nutils import _, log # error test input if left.rational or right.rational or top.rational or bottom.rational: raise RuntimeError('poisson_patch not supported for rational splines') # these are given as a oriented loop, so make all run in positive parametric direction top.reverse() left.reverse() # in order to add spline surfaces, they need identical parametrization Curve.make_splines_identical(top, bottom) Curve.make_splines_identical(left, right) # create computational (nutils) mesh p1 = bottom.order(0) p2 = left.order(0) n1 = len(bottom) n2 = len(left) dim= left.dimension k1 = bottom.knots(0) k2 = left.knots(0) m1 = [bottom.order(0) - bottom.continuity(k) - 1 for k in k1] m2 = [left.order(0) - left.continuity(k) - 1 for k in k2] domain, geom = mesh.rectilinear([k1, k2]) basis = domain.basis('spline', [p1-1, p2-1], knotmultiplicities=[m1,m2]) # assemble system matrix grad = basis.grad(geom) outer = fn.outer(grad,grad) integrand = outer.sum(-1) matrix = domain.integrate(integrand, geometry=geom, ischeme='gauss'+str(max(p1,p2)+1)) # initialize variables controlpoints = np.zeros((n1,n2,dim)) rhs = np.zeros((n1*n2)) constraints = np.array([[np.nan]*n2]*n1) # treat all dimensions independently for d in range(dim): # add boundary conditions constraints[ 0, :] = left[ :,d] constraints[-1, :] = right[ :,d] constraints[ :, 0] = bottom[:,d] constraints[ :,-1] = top[ :,d] # solve system lhs = matrix.solve(rhs, constrain=np.ndarray.flatten(constraints), solver='cg', tol=state.controlpoint_absolute_tolerance) # wrap results into splipy datastructures controlpoints[:,:,d] = np.reshape(lhs, (n1,n2), order='C') return Surface(bottom.bases[0], left.bases[0], controlpoints, bottom.rational, raw=True)
def __init__(self, nelems=60, rmin=1, rmax=10, piola=True): domain, refgeom, geom = mk_mesh(nelems, nelems, rmin, rmax) NutilsCase.__init__(self, 'ALE Flow around cylinder', domain, geom) self._refgeom = refgeom V = self.parameters.add('velocity', 1.0, 20.0) NU = 1 / self.parameters.add('viscosity', 100.0, 150.0) TIME = self.parameters.add('time', 0.0, 25.0, default=0.0) self.geometry += 1, geom # Add bases and construct a lift function vbasis, pbasis = mk_bases(self, piola) mk_lift(self, V, None, TIME) self['divergence'] -= 1, fn.outer(vbasis.div(geom), pbasis) self['divergence'].freeze(lift=(1, )) self['laplacian'] += NU, fn.outer(vbasis.grad(geom)).sum((-1, -2)) self['convection'] += 1, NutilsDelayedIntegrand('w_ia u_jb v_ka,b', 'ijk', 'wuv', x=geom, w=vbasis, u=vbasis, v=vbasis) # self['convection'] += 0.5, NutilsDelayedIntegrand( # 'w_ia u_jb v_ka,b - w_ia,b u_jb v_ka', 'ijk', 'wuv', # x=geom, w=vbasis, u=vbasis, v=vbasis # ) # self['convection'] += 0.5, NutilsDelayedIntegrand( # '(w_il v_kl) u_jm n_m', 'ijk', 'wuv', # n=geom.normal(), x=geom, w=vbasis, u=vbasis, v=vbasis, # ).prop(domain=domain.boundary['left']) self['v-h1s'] += 1, fn.outer(vbasis.grad(geom)).sum((-1, -2)) self['v-l2'] += 1, fn.outer(vbasis).sum((-1, )) self['p-l2'] += 1, fn.outer(pbasis) # forcing = fn.matmat(vbasis, fn.asarray([0, 1])) # self['forcing'] += FREQ**2 * (FREQ * TIME).sin(), forcing mk_force(self, geom, vbasis, pbasis)
def test_laplacian_matrix(mu, case): vbasis, pbasis = case.bases['v'].obj, case.bases['p'].obj trfgeom = case.geometry(mu) itg = fn.outer(vbasis.grad(trfgeom)).sum([-1, -2]) phys_mx = case.domain.integrate(itg * fn.J(trfgeom), ischeme='gauss9') test_mx = case['laplacian'](mu) np.testing.assert_almost_equal(phys_mx.export('dense'), test_mx.toarray())
def test_divergence_matrix(mu, case): vbasis, pbasis = case.bases['v'].obj, case.bases['p'].obj trfgeom = case.geometry(mu) itg = -fn.outer(vbasis.div(trfgeom), pbasis) phys_mx = case.domain.integrate(itg * fn.J(trfgeom), ischeme='gauss9') test_mx = case['divergence'](mu) np.testing.assert_almost_equal(phys_mx.export('dense'), test_mx.toarray())
def test_mass_matrix(mu, case): p_vbasis, p_pbasis = piola_bases(mu, case) trfgeom = case.geometry(mu) itg = fn.outer(p_vbasis.grad(trfgeom)).sum([-1, -2]) phys_mx = case.domain.integrate(itg * fn.J(trfgeom), ischeme='gauss9') test_mx = case['v-h1s'](mu) np.testing.assert_almost_equal(phys_mx.export('dense'), test_mx.toarray())
def setUp(self): super().setUp() domain, geom = mesh.rectilinear([numpy.linspace(0,1,9)] * 2) ubasis = domain.basis('std', degree=2).vector(2) u = ubasis.dot(function.Argument('dofs', [len(ubasis)])) Geom = geom * [1.1, 1] + u self.cons = solver.minimize('dofs', domain.boundary['left,right'].integral((u**2).sum(0), degree=4), droptol=1e-15).solve() self.boolcons = ~numpy.isnan(self.cons) strain = .5 * (function.outer(Geom.grad(geom), axis=1).sum(0) - function.eye(2)) self.energy = domain.integral(((strain**2).sum([0,1]) + 20*(function.determinant(Geom.grad(geom))-1)**2)*function.J(geom), degree=6) self.residual = self.energy.derivative('dofs') self.tol = 1e-10
class NSDivergence(MuCallable): _ident_ = 'NSDivergence' def __init__(self, n, *deps, scale=1): super().__init__((n, n), deps, scale=scale) def evaluate(self, case, mu, cont): geom = case['geometry'](mu) vbasis = case.basis('v', mu) pbasis = case.basis('p', mu) itg = -fn.outer(vbasis.div(geom), pbasis) itg = util.contract(itg, cont) with matrix.Scipy(): return unwrap(case.domain.integrate(itg * fn.J(geom), ischeme='gauss9'))
def test_nonlinear_diagonalshift(self): nelems = 10 domain, geom = mesh.rectilinear([nelems,1]) geom *= [2*numpy.pi/nelems, 1] ubasis = domain.basis('spline', degree=2).vector(2) u = ubasis.dot(function.Argument('dofs', [len(ubasis)])) Geom = [.5 * geom[0], geom[1] + function.cos(geom[0])] + u # compress by 50% and buckle cons = solver.minimize('dofs', domain.boundary['left,right'].integral((u**2).sum(0), degree=4), droptol=1e-15).solve() strain = .5 * (function.outer(Geom.grad(geom), axis=1).sum(0) - function.eye(2)) energy = domain.integral(((strain**2).sum([0,1]) + 150*(function.determinant(Geom.grad(geom))-1)**2)*function.J(geom), degree=6) nshift = 0 for iiter, (lhs, info) in enumerate(solver.minimize('dofs', energy, constrain=cons)): self.assertLess(iiter, 90) if info.shift: nshift += 1 if info.resnorm < self.tol: break self.assertEqual(nshift, 60)
def main( nelems: 'number of elements' = 12, lmbda: 'first lamé constant' = 1., mu: 'second lamé constant' = 1., degree: 'polynomial degree' = 2, withplots: 'create plots' = True, solvetol: 'solver tolerance' = 1e-10, ): # construct mesh verts = numpy.linspace(0, 1, nelems + 1) domain, geom = mesh.rectilinear([verts, verts]) # prepare basis dbasis = domain.basis('spline', degree=degree).vector(2) # construct matrix stress = library.Hooke(lmbda=lmbda, mu=mu) elasticity = function.outer(dbasis.grad(geom), stress(dbasis.symgrad(geom))).sum([2, 3]) matrix = domain.integrate(elasticity, geometry=geom, ischeme='gauss2') # construct dirichlet boundary constraints cons = domain.boundary['left'].project( 0, geometry=geom, onto=dbasis, ischeme='gauss2' ) \ | domain.boundary['right'].project( .5, geometry=geom, onto=dbasis.dotnorm(geom), ischeme='gauss2' ) # solve system lhs = matrix.solve(constrain=cons, tol=solvetol, symmetric=True, precon='diag') # construct solution function disp = dbasis.dot(lhs) # plot solution if withplots: makeplots(domain, geom + disp, stress(disp.symgrad(geom))) return lhs, cons
npts = len(velocity) // 2 velocity = velocity[:npts] pressure = pressure[:npts] linbasis = case.domain.basis('spline', degree=1) vsol = linbasis.dot(velocity[:,0])[_] * (1,0) + linbasis.dot(velocity[:,1])[_] * (0,1) psol = linbasis.dot(pressure) geom = case.physical_geometry(param) vbasis = case.basis('v', param) vgrad = vbasis.grad(geom) pbasis = case.basis('p', param) lhs = case.domain.project(psol, onto=pbasis, geometry=geom, ischeme='gauss9') vind = case.basis_indices('v') itg = fn.outer(vbasis).sum([-1]) + fn.outer(vgrad).sum([-1,-2]) with matrix.Scipy(): mx = case.domain.integrate(itg * fn.J(geom), ischeme='gauss9').core[np.ix_(vind,vind)] itg = (vbasis * vsol[_,:]).sum([-1]) + (vgrad * vsol.grad(geom)[_,:,:]).sum([-1,-2]) rhs = case.domain.integrate(itg * fn.J(geom), ischeme='gauss9')[vind] lhs[vind] = matrix.ScipyMatrix(mx).solve(rhs) lift = case._lift(param) solutions.append((lhs - lift) * weight) solutions = np.array(solutions) supremizers = ens.make_ensemble( case, solvers.supremizer, scheme, weights=False, parallel=False, args=[solutions], ) return scheme, solutions, supremizers
def __init__(self, refine=1, degree=4, nel=None): if nel is None: nel = int(10 * refine) pts = np.linspace(0, 1, nel + 1) domain, geom = mesh.rectilinear([pts, pts]) NutilsCase.__init__(self, 'Cavity flow', domain, geom, geom) bases = [ domain.basis('spline', degree=(degree, degree - 1)), # vx domain.basis('spline', degree=(degree - 1, degree)), # vy domain.basis('spline', degree=degree - 1), # pressure [1], # lagrange multiplier [0] * 4, # stabilization terms ] basis_lens = [len(b) for b in bases] vxbasis, vybasis, pbasis, lbasis, __ = fn.chain(bases) vbasis = vxbasis[:, _] * (1, 0) + vybasis[:, _] * (0, 1) self.bases.add('v', vbasis, length=sum(basis_lens[:2])) self.bases.add('p', pbasis, length=basis_lens[2]) self.extra_dofs = 5 self.constrain('v', 'left', 'top', 'bottom', 'right') self.integrals['lift'] = Affine(1, np.zeros(vbasis.shape[0])) self.integrals['geometry'] = Affine(1, geom) x, y = geom f = 4 * (x - x**2)**2 g = 4 * (y - y**2)**2 d1f = f.grad(geom)[0] d1g = g.grad(geom)[1] velocity = fn.asarray((f * d1g, -d1f * g)) pressure = d1f * d1g total = domain.integrate(pressure * fn.J(geom), ischeme='gauss9') pressure -= total / domain.volume(geometry=geom) force = pressure.grad(geom) - velocity.laplace(geom) self._exact_solutions = {'v': velocity, 'p': pressure} self.integrals['forcing'] = AffineIntegral(1, (vbasis * force[_, :]).sum(-1)) self.integrals['divergence'] = AffineIntegral( -1, fn.outer(vbasis.div(geom), pbasis)) self.integrals['laplacian'] = AffineIntegral( 1, fn.outer(vbasis.grad(geom)).sum((-1, -2))) self.integrals['v-h1s'] = AffineIntegral( 1, fn.outer(vbasis.grad(geom)).sum([-1, -2])) self.integrals['p-l2'] = AffineIntegral(1, fn.outer(pbasis)) root = self.ndofs - self.extra_dofs points = [(0, (0, 0)), (nel - 1, (0, 1)), (nel * (nel - 1), (1, 0)), (nel**2 - 1, (1, 1))] eqn = (pbasis.grad(geom) - vbasis.laplace(geom))[:, 0, _] colloc = collocate(domain, eqn, points, root + 1, self.ndofs) self.integrals['stab-lhs'] = AffineIntegral(1, colloc, 1, fn.outer(lbasis, pbasis)) self.integrals['stab-rhs'] = AffineIntegral( 1, collocate(domain, force[0, _], points, root + 1, self.ndofs))
def __init__(self, nel=10, L=15, penalty=1e10, ratio=1000, override=False, finalize=True): L /= 5 hnel = int(L * 5 * nel // 2) xpts = np.linspace(0, L, 2 * hnel + 1) yzpts = np.linspace(0, 0.2, nel + 1) domain, geom = mesh.rectilinear([xpts, yzpts]) dom1 = domain[:hnel, :] dom2 = domain[hnel:, :] NutilsCase.__init__(self, 'Elastic split beam', domain, geom) E1 = self.parameters.add('ymod1', 1e10, 9e10) E2 = self.parameters.add('ymod2', 1e10 / ratio, 9e10 / ratio) NU = self.parameters.add('prat', 0.25, 0.42) F1 = self.parameters.add('force1', -1e8, 1e8) mults = [[2] + [1] * (hnel - 1) + [2] + [1] * (hnel - 1) + [2], [2] + [1] * (nel - 1) + [2]] basis = domain.basis('spline', degree=1, knotmultiplicities=mults).vector(2) blen = len(basis) basis, *__ = fn.chain([basis, [0] * (2 * (nel + 1))]) self.bases.add('u', basis, length=blen) self.extra_dofs = (nel + 1) * 2 self.lift += 1, np.zeros((len(basis), )) self.constrain('u', 'left') self.constrain('u', 'right') MU1 = E1 / (1 + NU) MU2 = E2 / (1 + NU) LAMBDA1 = E1 * NU / (1 + NU) / (1 - 2 * NU) LAMBDA2 = E2 * NU / (1 + NU) / (1 - 2 * NU) itg_mu = fn.outer(basis.symgrad(geom)).sum([-1, -2]) itg_la = fn.outer(basis.div(geom)) self['stiffness'] += MU1, NutilsArrayIntegrand(itg_mu).prop( domain=dom1) self['stiffness'] += MU2, NutilsArrayIntegrand(itg_mu).prop( domain=dom2) self['stiffness'] += LAMBDA1, NutilsArrayIntegrand(itg_la).prop( domain=dom1) self['stiffness'] += LAMBDA2, NutilsArrayIntegrand(itg_la).prop( domain=dom2) # Penalty term ... quite hacky K = blen // 2 ldofs = list(range(hnel * (nel + 1), (hnel + 1) * (nel + 1))) rdofs = list(range((hnel + 1) * (nel + 1), (hnel + 2) * (nel + 1))) ldofs += [k + K for k in ldofs] rdofs += [k + K for k in rdofs] L = len(ldofs) Linds = list(range(self.ndofs - L, self.ndofs)) pen = sp.coo_matrix(((np.ones(L), (Linds, ldofs))), shape=(self.ndofs, self.ndofs)) pen += sp.coo_matrix(((-np.ones(L), (Linds, rdofs))), shape=(self.ndofs, self.ndofs)) self['penalty'] += 1, ScipyArrayIntegrand(sp.csc_matrix(pen + pen.T)) # Even more hacky for the ROM part bnd_lft = domain[:hnel, :].boundary['right'] bnd_rgt = domain[hnel:, :].boundary['left'] pts, wts = bnd_lft.elements[0].reference.getischeme('gauss5') penalty_mx = np.zeros((self.ndofs, self.ndofs)) for el_lft, el_rgt in zip(bnd_lft.elements, bnd_rgt.elements): udiff = ( basis.eval(_points=pts, _transforms=(el_lft.transform, el_lft.opposite)) - basis.eval(_points=pts, _transforms=(el_rgt.transform, el_rgt.opposite))) penalty_mx += (udiff[:, :, _, :] * udiff[:, _, :, :] * wts[:, _, _, _]).sum((0, -1)) self['penalty-rom'] += 1e20, sp.csc_matrix(penalty_mx) normdot = fn.matmat(basis, geom.normal()) force_bnd = domain[hnel - 11:hnel - 1, :].boundary['top'] self['forcing'] += F1, NutilsArrayIntegrand(normdot).prop( domain=force_bnd) self['u-h1s'] = self['stiffness'] self['u-h1s-l'] = self['stiffness'] self['u-h1s-r'] = self['stiffness'] self['u-h1s-e'] = self['stiffness'] self.verify()
def __init__(self, nel=10, ndim=2, L=15, override=False, finalize=True, graded=False): L /= 5 if graded: # HACK! xpts = _graded(0, L, int(L * 3 * nel + 1), 1.04) yzpts = _graded(0, 0.1, nel // 2 + 1, 1.3) yzpts = np.hstack([yzpts[:-1], 0.2 - yzpts[::-1]]) else: xpts = np.linspace(0, L, int(L * 5 * nel + 1)) yzpts = np.linspace(0, 0.2, nel + 1) if ndim == 2: domain, geom = mesh.rectilinear([xpts, yzpts]) else: domain, geom = mesh.rectilinear([xpts, yzpts, yzpts]) NutilsCase.__init__(self, 'Elastic beam', domain, geom) E = self.parameters.add('ymod', 1e10, 9e10) NU = self.parameters.add('prat', 0.25, 0.42) F1 = self.parameters.add('force1', -0.4e6, 0.4e6) F2 = self.parameters.add('force2', -0.2e6, 0.2e6) if ndim == 3: F3 = self.parameters.add('force3', -0.2e6, 0.2e6) basis = domain.basis('spline', degree=1).vector(ndim) self.bases.add('u', basis, length=len(basis)) self.lift += 1, np.zeros((len(basis), )) self.constrain('u', 'left') MU = E / (1 + NU) LAMBDA = E * NU / (1 + NU) / (1 - 2 * NU) self['stiffness'] += MU, fn.outer(basis.symgrad(geom)).sum([-1, -2]) self['stiffness'] += LAMBDA, fn.outer(basis.div(geom)) normdot = fn.matmat(basis, geom.normal()) irgt = NutilsArrayIntegrand(normdot).prop( domain=domain.boundary['right']) ibtm = NutilsArrayIntegrand(normdot).prop( domain=domain.boundary['bottom']) itop = NutilsArrayIntegrand(normdot).prop( domain=domain.boundary['top']) self['forcing'] += F1, irgt self['forcing'] -= F2, ibtm self['forcing'] += F2, itop if ndim == 3: ifrt = NutilsArrayIntegrand(normdot).prop( domain=domain.boundary['front']) ibck = NutilsArrayIntegrand(normdot).prop( domain=domain.boundary['back']) self['forcing'] -= F3, ifrt self['forcing'] += F3, ibck self['u-h1s'] += 1, fn.outer(basis.grad(geom)).sum([-1, -2]) self.verify()
def __init__(self, refine=1, degree=3, nel_up=None, nel_length=None, nel_up_mid=None, nel_length_out=None, stabilize=True, override=True): if nel_up is None: nel_up = int(50 * refine) if nel_length is None: nel_length = int(50 * refine) if nel_up_mid is None: nel_up_mid = nel_up // 5 if nel_length_out is None: nel_length_out = 2 * nel_length // 5 domain, geom = mesh.multipatch( patches=[[[0,1],[4,5]], [[1,2],[5,6]], [[2,3],[6,7]], [[5,6],[8,9]]], nelems={ (0,1): nel_up, (4,5): nel_up, (2,3): nel_up, (6,7): nel_up, (1,2): nel_up_mid, (5,6): nel_up_mid, (8,9): nel_up_mid, (0,4): nel_length, (1,5): nel_length, (2,6): nel_length, (3,7): nel_length, (5,8): nel_length_out, (6,9): nel_length_out, }, patchverts=[ [-5,0], [-5,1], [-5,2], [-5,3], [0,0], [0,1], [0,2], [0,3], [2,1], [2,2], ] ) NutilsCase.__init__(self, 'T-shape channel', domain, geom, geom) NU = 1 / self.parameters.add('viscosity', 20, 50) H = self.parameters.add('height', 1, 5) V = self.parameters.add('velocity', 1, 5) bases = [ domain.basis('spline', degree=(degree, degree-1)), # vx domain.basis('spline', degree=(degree-1, degree)), # vy domain.basis('spline', degree=degree-1) # pressure ] if stabilize: bases.append([0] * 4) basis_lens = [len(b) for b in bases] vxbasis, vybasis, pbasis, *__ = fn.chain(bases) vbasis = vxbasis[:,_] * (1,0) + vybasis[:,_] * (0,1) self.bases.add('v', vbasis, length=sum(basis_lens[:2])) self.bases.add('p', pbasis, length=basis_lens[2]) self.extra_dofs = 4 if stabilize else 0 x, y = geom hy = fn.piecewise(y, (1,2), y-1, 0, y-2) self.integrals['geometry'] = Affine(1, geom, H - 1, fn.asarray((0, hy))) self.constrain( 'v', 'patch0-bottom', 'patch0-left', 'patch0-right', 'patch1-left', 'patch2-left', 'patch2-top', 'patch2-right', 'patch3-bottom', 'patch3-top', ) vgrad = vbasis.grad(geom) # Lifting function profile = fn.max(0, y/3 * (1-y/3) * (1-x))[_] * (1, 0) self.integrals['lift'] = Affine(V, self.project_lift(profile, 'v')) # Characteristic functions cp0, cp1, cp2, cp3 = [characteristic(domain, (i,)) for i in range(4)] cp02 = cp0 + cp2 cp13 = cp1 + cp3 # Stokes divergence term self.integrals['divergence'] = AffineIntegral( -(H-1), fn.outer(vgrad[:,0,0], pbasis) * cp02, -1, fn.outer(vbasis.div(geom), pbasis), ) # Stokes laplacian term self.integrals['laplacian'] = AffineIntegral( NU, fn.outer(vgrad).sum([-1,-2]) * cp13, NU*H, fn.outer(vgrad[:,:,0]).sum(-1) * cp02, NU/H, fn.outer(vgrad[:,:,1]).sum(-1) * cp02, ) # Navier-Stokes convective term args = ('ijk', 'wuv') kwargs = {'x': geom, 'w': vbasis, 'u': vbasis, 'v': vbasis} self.integrals['convection'] = AffineIntegral( H, NutilsDelayedIntegrand('c w_ia u_j0 v_ka,0', *args, **kwargs, c=cp02), 1, NutilsDelayedIntegrand('c w_ia u_j1 v_ka,1', *args, **kwargs, c=cp02), 1, NutilsDelayedIntegrand('c w_ia u_jb v_ka,b', *args, **kwargs, c=cp13), ) # Norms self.integrals['v-h1s'] = self.integrals['laplacian'] / NU self.integrals['v-l2'] = AffineIntegral( H, fn.outer(vbasis).sum(-1) * cp02, 1, fn.outer(vbasis).sum(-1) * cp13, ) self.integrals['p-l2'] = AffineIntegral( H, fn.outer(pbasis) * cp02, 1, fn.outer(pbasis) * cp13, ) if not stabilize: self.verify() return root = self.ndofs - self.extra_dofs points = [ (0, (0, 0)), (nel_up*(nel_length-1), (1, 0)), (nel_up*nel_length + nel_up_mid*nel_length + nel_up - 1, (0, 1)), (nel_up*nel_length*2 + nel_up_mid*nel_length - 1, (1, 1)) ] terms = [] eqn = vbasis[:,0].grad(geom).grad(geom) colloc = collocate(domain, eqn[:,0,0,_], points, root, self.ndofs) terms.extend([NU, (colloc + colloc.T)]) colloc = collocate(domain, eqn[:,1,1,_], points, root, self.ndofs) terms.extend([NU/H**2, (colloc + colloc.T)]) eqn = -pbasis.grad(geom)[:,0,_] colloc = collocate(domain, eqn, points, root, self.ndofs) terms.extend([1, colloc + colloc.T]) self.integrals['stab-lhs'] = AffineIntegral(*terms) self.verify()
def __init__(self, refine=1, degree=3, nel=None, power=3): if nel is None: nel = int(10 * refine) pts = np.linspace(0, 1, nel + 1) domain, geom = mesh.rectilinear([pts, pts]) x, y = geom NutilsCase.__init__(self, 'Exact divergence-conforming flow', domain, geom, geom) w = self.parameters.add('w', 1, 2) h = self.parameters.add('h', 1, 2) bases = [ domain.basis('spline', degree=(degree, degree-1)), # vx domain.basis('spline', degree=(degree-1, degree)), # vy domain.basis('spline', degree=degree-1), # pressure [1], # lagrange multiplier [0] * 4, # stabilization terms ] basis_lens = [len(b) for b in bases] vxbasis, vybasis, pbasis, lbasis, __ = fn.chain(bases) vbasis = vxbasis[:,_] * (1,0) + vybasis[:,_] * (0,1) self.bases.add('v', vbasis, length=sum(basis_lens[:2])) self.bases.add('p', pbasis, length=basis_lens[2]) self.extra_dofs = 5 self.integrals['geometry'] = Affine( 1, geom, w-1, fn.asarray((x,0)), h-1, fn.asarray((0,y)), ) self.constrain('v', 'left', 'top', 'bottom', 'right') r = power self.power = power # Exact solution f = x**r g = y**r f1 = r * x**(r-1) g1 = r * y**(r-1) g2 = r*(r-1) * y**(r-2) f3 = r*(r-1)*(r-2) * x**(r-3) g3 = r*(r-1)*(r-2) * y**(r-3) self._exact_solutions = {'v': fn.asarray((f*g1, -f1*g)), 'p': f1*g1 - 1} # Awkward way of computing a solenoidal lift mdom, t = mesh.rectilinear([pts]) hbasis = mdom.basis('spline', degree=degree) hcoeffs = mdom.project(t[0]**r, onto=hbasis, geometry=t, ischeme='gauss9') projtderiv = hbasis.dot(hcoeffs).grad(t)[0] zbasis = mdom.basis('spline', degree=degree-1) zcoeffs = mdom.project(projtderiv, onto=zbasis, geometry=t, ischeme='gauss9') q = np.hstack([ np.outer(hcoeffs, zcoeffs).flatten(), - np.outer(zcoeffs, hcoeffs).flatten(), np.zeros((sum(basis_lens) - len(hcoeffs) * len(zcoeffs) * 2)) ]) self.integrals['lift'] = Affine(w**(r-1) * h**(r-1), q) self.integrals['forcing'] = AffineIntegral( w**(r-2) * h**(r+2), vybasis * (f3 * g)[_], w**r * h**r, 2*vybasis * (f1*g2)[_], w**(r+2) * h**(r-2), -vxbasis * (f*g3)[_], ) vx_x = vxbasis.grad(geom)[:,0] vx_xx = vx_x.grad(geom)[:,0] vx_y = vxbasis.grad(geom)[:,1] vx_yy = vx_y.grad(geom)[:,1] vy_x = vybasis.grad(geom)[:,0] vy_y = vybasis.grad(geom)[:,1] p_x = pbasis.grad(geom)[:,0] self.integrals['laplacian'] = AffineIntegral( h * w, fn.outer(vx_x, vx_x), h**3 / w, fn.outer(vy_x, vy_x), w**3 / h, fn.outer(vx_y, vx_y), w * h, fn.outer(vy_y, vy_y), ) self.integrals['divergence'] = AffineIntegral( h * w, (fn.outer(vx_x, pbasis) + fn.outer(vy_y, pbasis)) ) self['v-h1s'] = AffineIntegral(self.integrals['laplacian']) self['p-l2'] = AffineIntegral(h * w, fn.outer(pbasis, pbasis)) root = self.ndofs - self.extra_dofs points = [(0, (0, 0)), (nel-1, (0, 1)), (nel*(nel-1), (1, 0)), (nel**2-1, (1, 1))] ca, cb, cc = [ collocate(domain, eqn[:,_], points, root+1, self.ndofs) for eqn in [p_x, -vx_xx, -vx_yy] ] self.integrals['stab-lhs'] = AffineIntegral( 1/w, ca, 1/w, cb, w/h**2, cc, 1, fn.outer(lbasis, pbasis), w**3 * h**(r-3), collocate(domain, -f*g3[_], points, root+1, self.ndofs), ) self.integrals['v-trf'] = Affine( w, fn.asarray([[1,0], [0,0]]), h, fn.asarray([[0,0], [0,1]]), )
def metric_tensor(g, c): jacT = function.transpose(jacobian(g, c)) return function.outer(jacT, jacT).sum(-1)
@pytest.fixture def mu(): return { 'viscosity': 1.0, 'length': 10.0, 'height': 1.5, 'velocity': 1.0, } def test_divergence_matrix(case, mu): domain, vbasis, pbasis = case.domain, case.bases['v'].obj, case.bases[ 'p'].obj geom = case.geometry(mu) itg = -fn.outer(vbasis.div(geom), pbasis) phys_mx = domain.integrate(itg * fn.J(geom), ischeme='gauss9').export('dense') test_mx = case['divergence'](mu).toarray() np.testing.assert_almost_equal(phys_mx, test_mx) def test_laplacian_matrix(case, mu): domain, vbasis, pbasis = case.domain, case.bases['v'].obj, case.bases[ 'p'].obj geom = case.geometry(mu) itg = fn.outer(vbasis.grad(geom)).sum([-1, -2]) / mu['viscosity'] phys_mx = domain.integrate(itg * fn.J(geom), ischeme='gauss9').export('dense')
] vnbasis, vtbasis, pbasis = fn.chain(bases) vbasis = vnbasis + vtbasis case.bases.add('v', vbasis, length=len(bases[0]) + len(bases[1])) case.bases.add('p', pbasis, length=len(bases[2])) return vbasis, pbasis def mk_force(case, geom, vbasis, pbasis): domain = case.domain with matrix.Scipy(): lapl = domain.integrate(fn.outer(vbasis.grad(geom)).sum((-1, -2)), geometry=geom, ischeme='gauss9') zero = np.zeros((lapl.shape[0], )) xcons = domain.boundary['bottom'].project((1, 0), onto=vbasis, geometry=geom, ischeme='gauss9') xcons[np.where(np.isnan(xcons))] = 0.0 ycons = domain.boundary['bottom'].project((0, 1), onto=vbasis, geometry=geom, ischeme='gauss9') ycons[np.where(np.isnan(ycons))] = 0.0
def elliptic_control_mapping(g, f, eps=1e-4, ltol=1e-5, degree=None, **solveargs): ''' Nutils implementation of the corresponding method from the ``fsol`` module. It's slower but accepts any control mapping function. Unline in the ``fsol`` case, all derivatives are computed automatically. ''' assert len(g) == len(f) == 2 assert eps >= 0 if degree is None: degree = 4 * g.ischeme if isinstance(f, go.TensorGridObject): if not (f.knotvector <= g.knotvector).all(): raise AssertionError(''' Error, the control-mapping knotvector needs to be a subset of the target GridObject knotvector ''') # refine, just in case f = go.refine_GridObject(f, g.knotvector) G = metric_tensor(g, f.x) else: # f is a nutils function jacT = function.transpose(f.grad(g.geom)) G = function.outer(jacT, jacT).sum(-1) target = function.Argument('target', [len(g.x)]) basis = g.basis.vector(2) x = basis.dot(target) (g11, g12), (g21, g22) = metric_tensor(g, target) lapl = g22 * x.grad(g.geom)[:, 0].grad(g.geom)[:, 0] - \ 2 * g12 * x.grad(g.geom)[:, 0].grad(g.geom)[:, 1] + \ g11 * x.grad(g.geom)[:, 1].grad(g.geom)[:, 1] G_tilde = G / function.sqrt(function.determinant(G)) (G11, G12), (G12, G22) = G_tilde P = g11 * G12.grad(g.geom)[1] - g12 * G11.grad(g.geom)[1] Q = 0.5 * (g11 * G22.grad(g.geom)[0] - g22 * G11.grad(g.geom)[0]) S = g12 * G22.grad(g.geom)[0] - g22 * G12.grad(g.geom)[0] R = 0.5 * (g11 * G22.grad(g.geom)[1] - g22 * G11.grad(g.geom)[1]) control_term = -x.grad( g.geom )[:, 0] * ( G22*(P-Q) + G12*(S-R) ) \ -x.grad( g.geom )[:, 1] * ( G11*(R-S) + G12*(Q-P) ) scale = g11 + g22 + eps res = g.domain.integral((basis * (control_term + lapl)).sum(-1) / scale, geometry=g.geom, degree=degree) init, cons = g.x, g.cons lhs = solver.newton('target', res, lhs0=init, constrain=cons.where).solve(ltol, **solveargs) g.x = lhs
def main( nelems: 'number of elements' = 12, viscosity: 'fluid viscosity' = 1e-2, density: 'fluid density' = 1, tol: 'solver tolerance' = 1e-12, rotation: 'cylinder rotation speed' = 0, timestep: 'time step' = 1/24, maxradius: 'approximate domain size' = 25, tmax: 'end time' = numpy.inf, withplots: 'create plots' = True, ): uinf = numpy.array([ 1, 0 ]) log.user( 'reynolds number:', density/viscosity ) # construct mesh rscale = numpy.pi / nelems melems = numpy.ceil( numpy.log(2*maxradius) / rscale ).astype( int ) log.info( 'creating {}x{} mesh, outer radius {:.2f}'.format( melems, 2*nelems, .5*numpy.exp(rscale*melems) ) ) domain, gridgeom = mesh.rectilinear( [range(melems+1),numpy.linspace(0,2*numpy.pi,2*nelems+1)], periodic=(1,) ) rho, phi = gridgeom phi += 1e-3 # tiny nudge (0.057 deg) to break element symmetry cylvelo = -.5 * rotation * function.trignormal(phi) radius = .5 * function.exp( rscale * rho ) geom = radius * function.trigtangent(phi) # prepare bases J = geom.grad( gridgeom ) detJ = function.determinant( J ) vnbasis, vtbasis, pbasis = function.chain([ # compatible spaces using piola transformation domain.basis( 'spline', degree=(3,2), removedofs=((0,),None) )[:,_] * J[:,0] / detJ, domain.basis( 'spline', degree=(2,3) )[:,_] * J[:,1] / detJ, domain.basis( 'spline', degree=2 ) / detJ, ]) vbasis = vnbasis + vtbasis stressbasis = (2*viscosity) * vbasis.symgrad(geom) - pbasis[:,_,_] * function.eye( domain.ndims ) # prepare matrices A = function.outer( vbasis.grad(geom), stressbasis ).sum([2,3]) + function.outer( pbasis, vbasis.div(geom) ) Ao = function.outer( vbasis, density * ( vbasis.grad(geom) * uinf ).sum(-1) ).sum(-1) At = (density/timestep) * function.outer( vbasis ).sum(-1) Ad = function.outer( vbasis.div(geom) ) stokesmat, uniconvec, inertmat, divmat = domain.integrate( [ A, Ao, At, Ad ], geometry=geom, ischeme='gauss9' ) # interior boundary condition (weak imposition of shear verlocity component) inner = domain.boundary['left'] h = inner.elem_eval( 1, geometry=geom, ischeme='gauss9', asfunction=True ) nietzsche = (2*viscosity) * ( (7.5/h) * vbasis - vbasis.symgrad(geom).dotnorm(geom) ) B = function.outer( nietzsche, vbasis ).sum(-1) b = ( nietzsche * cylvelo ).sum(-1) bcondmat, rhs = inner.integrate( [ B, b ], geometry=geom, ischeme='gauss9' ) stokesmat += bcondmat # exterior boundary condition (full velocity vector imposed at inflow) inflow = domain.boundary['right'].select( -( uinf * geom.normal() ).sum(-1), ischeme='gauss1' ) cons = inflow.project( uinf, onto=vbasis, geometry=geom, ischeme='gauss9', tol=1e-12 ) # initial condition (stationary oseen flow) lhs = (stokesmat+uniconvec).solve( rhs, constrain=cons, tol=0 ) # prepare plotting if not withplots: makeplots = lambda *args: None else: makeplots = MakePlots( domain, geom, video=withplots=='video', timestep=timestep ) mesh_ = domain.elem_eval( geom, ischeme='bezier5', separate=True ) inflow_ = inflow.elem_eval( geom, ischeme='bezier5', separate=True ) with plot.PyPlot( 'mesh', ndigits=0 ) as plt: plt.mesh( mesh_ ) plt.rectangle( makeplots.bbox[:,0], *( makeplots.bbox[:,1] - makeplots.bbox[:,0] ), ec='green' ) plt.segments( inflow_, colors='red' ) # start time stepping stokesmat += inertmat precon = (stokesmat+uniconvec).getprecon( 'splu', constrain=cons ) for istep in log.count( 'timestep', start=1 ): log.info( 'velocity divergence:', divmat.matvec(lhs).dot(lhs) ) convection = density * ( vbasis.grad(geom) * vbasis.dot(lhs) ).sum(-1) matrix = stokesmat + domain.integrate( function.outer( vbasis, convection ).sum(-1), ischeme='gauss9', geometry=geom ) lhs = matrix.solve( rhs + inertmat.matvec(lhs), lhs0=lhs, constrain=cons, tol=tol, restart=9999, precon=precon ) makeplots( vbasis.dot(lhs), pbasis.dot(lhs), istep*timestep*rotation ) if istep*timestep > tmax: break return lhs
def __init__(self, nelems=10, piola=False, degree=2): pts = np.linspace(0, 1, nelems + 1) domain, geom = mesh.rectilinear([pts, pts]) NutilsCase.__init__(self, 'Abdulahque Manufactured Solution', domain, geom) RE = 1 / self.parameters.add('re', 100.0, 150.0) T = self.parameters.add('time', 0.0, 10.0) # Add bases and construct a lift function vbasis, pbasis = mk_bases(self, piola, degree) self.constrain('v', 'bottom') # self.constrain('v', 'top') # self.constrain('v', 'left') # self.constrain('v', 'right') self.lift += 1, np.zeros(len(vbasis)) self['divergence'] -= 1, fn.outer(vbasis.div(geom), pbasis) self['divergence'].freeze(lift=(1, )) self['laplacian'] += RE, fn.outer(vbasis.grad(geom)).sum((-1, -2)) self['convection'] += 1, NutilsDelayedIntegrand('w_ia u_jb v_ka,b', 'ijk', 'wuv', x=geom, w=vbasis, u=vbasis, v=vbasis) self['v-h1s'] += 1, fn.outer(vbasis.grad(geom)).sum((-1, -2)) self['v-l2'] += 1, fn.outer(vbasis).sum((-1, )) self['p-l2'] += 1, fn.outer(pbasis) # Body force x, y = geom h = T h1 = 1 f = 4 * (x - x**2)**2 f1 = f.grad(geom)[0] f2 = f1.grad(geom)[0] f3 = f2.grad(geom)[0] g = 4 * (y - y**2)**2 g1 = g.grad(geom)[1] g2 = g1.grad(geom)[1] g3 = g2.grad(geom)[1] # Body force self['forcing'] += h**2, fn.matmat( vbasis, fn.asarray([ (g1**2 - g * g2) * f * f1, (f1**2 - f * f2) * g * g1, ])) self['forcing'] += RE * h, fn.matmat( vbasis, fn.asarray([-f * g3, f3 * g + 2 * f1 * g2])) self['forcing'] += h1, fn.matmat(vbasis, fn.asarray([f * g1, -f1 * g])) # Neumann conditions mx = fn.asarray([[f1 * g1, f * g2], [-f2 * g, -f1 * g1]]) hh = fn.matmat(mx, geom.normal()) - f1 * g1 * geom.normal() self['forcing'] += RE * h, NutilsArrayIntegrand(fn.matmat( vbasis, hh)).prop(domain=domain.boundary['left'] | domain.boundary['right'] | domain.boundary['top'])
vbasis, pbasis = case.bases['v'].obj, case.bases['p'].obj with matrix.Scipy(): cons = domain.boundary['left'].project((0, 0), onto=vbasis, geometry=geom, ischeme='gauss1') cons_x = domain.boundary['right'].select(-x).project( (1, 0), onto=vbasis, geometry=geom, ischeme='gauss9', constrain=cons, ) mx = fn.outer(vbasis.grad(geom)).sum([-1, -2]) mx -= fn.outer(pbasis, vbasis.div(geom)) mx -= fn.outer(vbasis.div(geom), pbasis) with matrix.Scipy(): mx = domain.integrate(mx, geometry=geom, ischeme='gauss9') rhs = np.zeros(pbasis.shape) lhs_x = mx.solve(rhs, constrain=cons_x) lhs_x[case.bases['p'].indices] = 0.0 case.lift += V, lhs_x case.constrain('v', 'left') case.constrain('v', domain.boundary['right'].select(-x)) def mk_force(case, geom, vbasis, pbasis):
def main( nelems: 'number of elements' = 20, epsilon: 'epsilon, 0 for automatic (based on nelems)' = 0, timestep: 'time step' = .01, maxtime: 'end time' = 1., theta: 'contact angle (degrees)' = 90, init: 'initial condition (random/bubbles)' = 'random', withplots: 'create plots' = True, ): mineps = 1. / nelems if not epsilon: log.info('setting epsilon=%f' % mineps) epsilon = mineps elif epsilon < mineps: log.warning('epsilon under crititical threshold: %f < %f' % (epsilon, mineps)) ewall = .5 * numpy.cos(theta * numpy.pi / 180) # construct mesh xnodes = ynodes = numpy.linspace(0, 1, nelems + 1) domain, geom = mesh.rectilinear([xnodes, ynodes]) # prepare bases cbasis, mubasis = function.chain( [domain.basis('spline', degree=2), domain.basis('spline', degree=2)]) # define mixing energy and splitting: F' = f_p - f_n F = lambda c_: (.5 / epsilon**2) * (c_**2 - 1)**2 f_p = lambda c_: (1. / epsilon**2) * 4 * c_ f_n = lambda c_: (1. / epsilon**2) * (6 * c_ - 2 * c_**3) # prepare matrix A = function.outer( cbasis ) \ + (timestep*epsilon**2) * function.outer( cbasis.grad(geom), mubasis.grad(geom) ).sum(-1) \ + function.outer( mubasis, mubasis - f_p(cbasis) ) \ - function.outer( mubasis.grad(geom), cbasis.grad(geom) ).sum(-1) matrix = domain.integrate(A, geometry=geom, ischeme='gauss4') # prepare wall energy right hand side rhs0 = domain.boundary.integrate(mubasis * ewall, geometry=geom, ischeme='gauss4') # construct initial condition if init == 'random': numpy.random.seed(0) c = cbasis.dot(numpy.random.normal(0, .5, cbasis.shape)) elif init == 'bubbles': R1 = .25 R2 = numpy.sqrt(.5) * R1 # area2 = .5 * area1 c = 1 + function.tanh( (R1-function.norm2(geom-(.5+R2/numpy.sqrt(2)+.8*epsilon)))/epsilon ) \ + function.tanh( (R2-function.norm2(geom-(.5-R1/numpy.sqrt(2)-.8*epsilon)))/epsilon ) else: raise Exception('unknown init %r' % init) # prepare plotting nsteps = numeric.round(maxtime / timestep) makeplots = MakePlots(domain, geom, nsteps, video=withplots == 'video') if withplots else lambda *args: None # start time stepping for istep in log.range('timestep', nsteps): Emix = F(c) Eiface = .5 * (c.grad(geom)**2).sum(-1) Ewall = (abs(ewall) + ewall * c) b = cbasis * c - mubasis * f_n(c) rhs, total, energy_mix, energy_iface = domain.integrate( [b, c, Emix, Eiface], geometry=geom, ischeme='gauss4') energy_wall = domain.boundary.integrate(Ewall, geometry=geom, ischeme='gauss4') log.user('concentration {}, energy {}'.format( total, energy_mix + energy_iface + energy_wall)) lhs = matrix.solve(rhs0 + rhs, tol=1e-12, restart=999) c = cbasis.dot(lhs) mu = mubasis.dot(lhs) makeplots(c, mu, energy_mix, energy_iface, energy_wall) return lhs, energy_mix, energy_iface, energy_wall
def main( L: 'domain size' = 4., R: 'hole radius' = 1., E: "young's modulus" = 1e5, nu: "poisson's ratio" = 0.3, T: 'far field traction' = 10, nr: 'number of h-refinements' = 2, withplots: 'create plots' = True, ): #Create the coarsest level parameter domain domain, geometry = mesh.rectilinear([1, 2]) #Define the control points and control point weights controlpoints = numpy.array([[0, R], [R * (1 - 2**.5), R], [-R, R * (2**.5 - 1)], [-R, 0], [0, .5 * (R + L)], [-.15 * (R + L), .5 * (R + L)], [-.5 * (R + L), .15 * (R * L)], [-.5 * (R + L), 0], [0, L], [-L, L], [-L, L], [-L, 0]]) weights = numpy.array( [1, .5 + .5 / 2**.5, .5 + .5 / 2**.5, 1, 1, 1, 1, 1, 1, 1, 1, 1]) #Create the second-order B-spline basis over the coarsest domain bsplinebasis = domain.basis('spline', degree=2) #Create the NURBS basis weightfunc = bsplinebasis.dot(weights) nurbsbasis = (bsplinebasis * weights) / weightfunc #Create the isogeometric map geometry = (nurbsbasis[:, _] * controlpoints).sum(0) #Create the computational domain by h-refinement domain = domain.refine(nr) #Create the B-spline basis bsplinebasis = domain.basis('spline', degree=2) #Create the NURBS basis weights = domain.project(weightfunc, onto=bsplinebasis, geometry=geometry, ischeme='gauss9') nurbsbasis = (bsplinebasis * weights) / weightfunc #Create the displacement field basis ubasis = nurbsbasis.vector(2) #Define the plane stress function stress = library.Hooke(lmbda=E * nu / (1 - nu**2), mu=.5 * E / (1 + nu)) #Get the exact solution uexact = exact_solution(geometry, T, R, E, nu) sigmaexact = stress(uexact.symgrad(geometry)) #Define the linear and bilinear forms mat_func = function.outer(ubasis.symgrad(geometry), stress(ubasis.symgrad(geometry))).sum([2, 3]) rhs_func = (ubasis * sigmaexact.dotnorm(geometry)).sum(-1) #Compute the matrix and rhs mat = domain.integrate(mat_func, geometry=geometry, ischeme='gauss9') rhs = domain.boundary['right'].integrate(rhs_func, geometry=geometry, ischeme='gauss9') #Compute the constraints vector for the symmetry conditions cons = domain.boundary['top,bottom'].project(0, onto=ubasis.dotnorm(geometry), geometry=geometry, ischeme='gauss9') #Solve the system of equations sol = mat.solve(rhs=rhs, constrain=cons) #Compute the approximate displacement and stress functions u = ubasis.dot(sol) sigma = stress(u.symgrad(geometry)) #Post-processing if withplots: points, colors = domain.simplex.elem_eval([geometry, sigma[0, 0]], ischeme='bezier8', separate=True) with plot.PyPlot('solution', index=nr) as plt: plt.mesh(points, colors) plt.colorbar() #Compute the L2-norm of the error in the stress err = numpy.sqrt( domain.integrate( ((sigma - sigmaexact) * (sigma - sigmaexact)).sum([0, 1]), geometry=geometry, ischeme='gauss9')) #Compute the mesh parameter (maximum physical distance between diagonally opposite knot locations) hmax = numpy.max([ max(numpy.linalg.norm(verts[0] - verts[3]), numpy.linalg.norm(verts[1] - verts[2])) for verts in domain.elem_eval(geometry, ischeme='bezier2', separate=True) ]) return err, hmax
def __init__(self, refine=1, degree=3, nel_up=None, nel_length=None, stabilize=True): if nel_up is None: nel_up = int(10 * refine) if nel_length is None: nel_length = int(100 * refine) domain, geom = mesh.multipatch( patches=[[[0, 1], [3, 4]], [[3, 4], [6, 7]], [[2, 3], [5, 6]]], nelems={ (0, 1): nel_up, (3, 4): nel_up, (6, 7): nel_up, (2, 5): nel_length, (3, 6): nel_length, (4, 7): nel_length, (0, 3): nel_up, (1, 4): nel_up, (2, 3): nel_up, (5, 6): nel_up, }, patchverts=[[-1, 0], [-1, 1], [0, -1], [0, 0], [0, 1], [1, -1], [1, 0], [1, 1]], ) NutilsCase.__init__(self, 'Backward-facing step channel', domain, geom, geom) NU = 1 / self.parameters.add('viscosity', 20, 50) L = self.parameters.add('length', 9, 12, 10) H = self.parameters.add('height', 0.3, 2, 1) V = self.parameters.add('velocity', 0.5, 1.2, 1) # Bases bases = [ domain.basis('spline', degree=(degree, degree - 1)), # vx domain.basis('spline', degree=(degree - 1, degree)), # vy domain.basis('spline', degree=degree - 1), # pressure ] if stabilize: bases.append([0] * 4) basis_lens = [len(b) for b in bases] vxbasis, vybasis, pbasis, *__ = fn.chain(bases) vbasis = vxbasis[:, _] * (1, 0) + vybasis[:, _] * (0, 1) self.bases.add('v', vbasis, length=sum(basis_lens[:2])) self.bases.add('p', pbasis, length=basis_lens[2]) self.extra_dofs = 4 if stabilize else 0 x, y = geom hx = fn.piecewise(x, (0, ), 0, x) hy = fn.piecewise(y, (0, ), y, 0) self.integrals['geometry'] = Affine( 1.0, geom, (L - 1), fn.asarray((hx, 0)), (H - 1), fn.asarray((0, hy)), ) self.constrain('v', 'patch0-bottom', 'patch0-top', 'patch0-left', 'patch1-top', 'patch2-bottom', 'patch2-left') vgrad = vbasis.grad(geom) # Lifting function profile = fn.max(0, y * (1 - y) * 4)[_] * (1, 0) self.integrals['lift'] = Affine(V, self.project_lift(profile, 'v')) # Characteristic functions cp0, cp1, cp2 = [characteristic(domain, (i, )) for i in range(3)] cp12 = cp1 + cp2 # Stokes divergence term self.integrals['divergence'] = AffineIntegral( -(H - 1), fn.outer(vgrad[:, 0, 0], pbasis) * cp2, -(L - 1), fn.outer(vgrad[:, 1, 1], pbasis) * cp12, -1, fn.outer(vbasis.div(geom), pbasis), ) # Stokes laplacian term self.integrals['laplacian'] = AffineIntegral( NU, fn.outer(vgrad).sum([-1, -2]) * cp0, NU / L, fn.outer(vgrad[:, :, 0]).sum(-1) * cp1, NU * L, fn.outer(vgrad[:, :, 1]).sum(-1) * cp1, NU * H / L, fn.outer(vgrad[:, :, 0]).sum(-1) * cp2, NU * L / H, fn.outer(vgrad[:, :, 1]).sum(-1) * cp2, ) # Navier-stokes convective term args = ('ijk', 'wuv') kwargs = {'x': geom, 'w': vbasis, 'u': vbasis, 'v': vbasis} self.integrals['convection'] = AffineIntegral( H, NutilsDelayedIntegrand('c w_ia u_j0 v_ka,0', *args, **kwargs, c=cp2), L, NutilsDelayedIntegrand('c w_ia u_j1 v_ka,1', *args, **kwargs, c=cp12), 1, NutilsDelayedIntegrand('c w_ia u_jb v_ka,b', *args, **kwargs, c=cp0), 1, NutilsDelayedIntegrand('c w_ia u_j0 v_ka,0', *args, **kwargs, c=cp1), ) # Norms self.integrals['v-h1s'] = self.integrals['laplacian'] / NU self.integrals['v-l2'] = AffineIntegral( 1, fn.outer(vbasis).sum(-1) * cp0, L, fn.outer(vbasis).sum(-1) * cp1, L * H, fn.outer(vbasis).sum(-1) * cp2, ) self.integrals['p-l2'] = AffineIntegral( 1, fn.outer(pbasis) * cp0, L, fn.outer(pbasis) * cp1, L * H, fn.outer(pbasis) * cp2, ) if not stabilize: self.verify return root = self.ndofs - self.extra_dofs terms = [] points = [(0, (0, 0)), (nel_up - 1, (0, 1))] eqn = vbasis.laplace(geom)[:, 0, _] colloc = collocate(domain, eqn, points, root, self.ndofs) terms.extend([NU, (colloc + colloc.T)]) eqn = -pbasis.grad(geom)[:, 0, _] colloc = collocate(domain, eqn, points, root, self.ndofs) terms.extend([1, colloc + colloc.T]) points = [(nel_up**2 + nel_up * nel_length, (0, 0))] eqn = vbasis[:, 0].grad(geom).grad(geom) colloc = collocate(domain, eqn[:, 0, 0, _], points, root + 2, self.ndofs) terms.extend([NU / L**2, colloc.T]) colloc = collocate(domain, eqn[:, 1, 1, _], points, root + 2, self.ndofs) terms.extend([NU / H**2, colloc]) eqn = -pbasis.grad(geom)[:, 0, _] colloc = collocate(domain, -pbasis.grad(geom)[:, 0, _], points, root + 2, self.ndofs) terms.extend([1 / L, colloc]) points = [(nel_up * (nel_up - 1), (1, 0))] colloc = collocate(domain, vbasis.laplace(geom)[:, 0, _], points, root + 3, self.ndofs) terms.extend([NU, colloc]) colloc = collocate(domain, -pbasis.grad(geom)[:, 0, _], points, root + 3, self.ndofs) terms.extend([1, colloc]) self.integrals['stab-lhs'] = AffineIntegral(*terms) self.verify()
def main( nelems: 'number of elements' = 12, viscosity: 'fluid viscosity' = 1e-3, density: 'fluid density' = 1, degree: 'polynomial degree' = 2, warp: 'warp domain (downward bend)' = False, tol: 'solver tolerance' = 1e-5, maxiter: 'maximum number if iterations, 0 for unlimited' = 0, withplots: 'create plots' = True, ): Re = density / viscosity # based on unit length and velocity log.user( 'reynolds number: {:.1f}'.format(Re) ) # construct mesh verts = numpy.linspace( 0, 1, nelems+1 ) domain, geom = mesh.rectilinear( [verts,verts] ) # construct bases vxbasis, vybasis, pbasis, lbasis = function.chain([ domain.basis( 'spline', degree=(degree+1,degree), removedofs=((0,-1),None) ), domain.basis( 'spline', degree=(degree,degree+1), removedofs=(None,(0,-1)) ), domain.basis( 'spline', degree=degree ), [1], # lagrange multiplier ]) if not warp: vbasis = function.stack( [ vxbasis, vybasis ], axis=1 ) else: gridgeom = geom xi, eta = gridgeom geom = (eta+2) * function.rotmat(xi*.4)[:,1] - (0,2) # slight downward bend J = geom.grad( gridgeom ) detJ = function.determinant( J ) vbasis = ( vxbasis[:,_] * J[:,0] + vybasis[:,_] * J[:,1] ) / detJ # piola transform pbasis /= detJ stressbasis = (2*viscosity) * vbasis.symgrad(geom) - (pbasis)[:,_,_] * function.eye( domain.ndims ) # construct matrices A = function.outer( vbasis.grad(geom), stressbasis ).sum([2,3]) \ + function.outer( pbasis, vbasis.div(geom)+lbasis ) \ + function.outer( lbasis, pbasis ) Ad = function.outer( vbasis.div(geom) ) stokesmat, divmat = domain.integrate( [ A, Ad ], geometry=geom, ischeme='gauss9' ) # define boundary conditions normal = geom.normal() utop = function.asarray([ normal[1], -normal[0] ]) h = domain.boundary.elem_eval( 1, geometry=geom, ischeme='gauss9', asfunction=True ) nietzsche = (2*viscosity) * ( ((degree+1)*2.5/h) * vbasis - vbasis.symgrad(geom).dotnorm(geom) ) stokesmat += domain.boundary.integrate( function.outer( nietzsche, vbasis ).sum(-1), geometry=geom, ischeme='gauss9' ) rhs = domain.boundary['top'].integrate( ( nietzsche * utop ).sum(-1), geometry=geom, ischeme='gauss9' ) # prepare plotting makeplots = MakePlots( domain, geom ) if withplots else lambda *args: None # start picard iterations lhs = stokesmat.solve( rhs, tol=tol, solver='cg', precon='spilu' ) for iiter in log.count( 'picard' ): log.info( 'velocity divergence:', divmat.matvec(lhs).dot(lhs) ) makeplots( vbasis.dot(lhs), pbasis.dot(lhs) ) ugradu = ( vbasis.grad(geom) * vbasis.dot(lhs) ).sum(-1) convection = density * function.outer( vbasis, ugradu ).sum(-1) matrix = stokesmat + domain.integrate( convection, ischeme='gauss9', geometry=geom ) lhs, info = matrix.solve( rhs, lhs0=lhs, tol=tol, info=True, precon='spilu', restart=999 ) if iiter == maxiter-1 or info.niter == 0: break return rhs, lhs