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, figures: 'create figures' = True, ): log.user('reynolds number: {:.1f}'.format( density / viscosity)) # based on unit length and velocity # create namespace ns = function.Namespace() ns.viscosity = viscosity ns.density = density # construct mesh verts = numpy.linspace(0, 1, nelems + 1) domain, ns.x0 = mesh.rectilinear([verts, verts]) # construct bases ns.uxbasis, ns.uybasis, ns.pbasis, ns.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 ]) ns.ubasis_ni = '<uxbasis_n, uybasis_n>_i' # construct geometry if not warp: ns.x = ns.x0 else: xi, eta = ns.x0 ns.x = (eta + 2) * function.rotmat(xi * .4)[:, 1] - ( 0, 2) # slight downward bend ns.J_ij = 'x_i,x0_j' ns.detJ = function.determinant(ns.J) ns.ubasis_ni = 'ubasis_nj J_ij / detJ' # piola transform ns.pbasis_n = 'pbasis_n / detJ' # populate namespace ns.u_i = 'ubasis_ni ?lhs_n' ns.p = 'pbasis_n ?lhs_n' ns.l = 'lbasis_n ?lhs_n' ns.sigma_ij = 'viscosity (u_i,j + u_j,i) - p δ_ij' ns.c = 5 * (degree + 1) / domain.boundary.elem_eval( 1, geometry=ns.x, ischeme='gauss2', asfunction=True) ns.nietzsche_ni = 'viscosity (c ubasis_ni - (ubasis_ni,j + ubasis_nj,i) n_j)' ns.top = domain.boundary.indicator('top') ns.utop_i = 'top <n_1, -n_0>_i' # solve stokes flow res = domain.integral( 'ubasis_ni,j sigma_ij + pbasis_n (u_k,k + l) + lbasis_n p' @ ns, geometry=ns.x, degree=2 * (degree + 1)) res += domain.boundary.integral('nietzsche_ni (u_i - utop_i)' @ ns, geometry=ns.x, degree=2 * (degree + 1)) lhs0 = solver.solve_linear('lhs', res) if figures: postprocess(domain, ns(lhs=lhs0)) # solve navier-stokes flow res += domain.integral('density ubasis_ni u_i,j u_j' @ ns, geometry=ns.x, degree=3 * (degree + 1)) lhs1 = solver.newton('lhs', res, lhs0=lhs0).solve(tol=1e-10) if figures: postprocess(domain, ns(lhs=lhs1)) return lhs0, lhs1
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