Esempio n. 1
0
def navierstokes():
  domain, geom = mesh.rectilinear( [numpy.linspace(0,1,9)] * 2 )
  ubasis, pbasis = function.chain([
    domain.basis( 'std', degree=2 ).vector(2),
    domain.basis( 'std', degree=1 ),
  ])
  dofs = function.DerivativeTarget( [len(ubasis)] )
  u = ubasis.dot( dofs )
  p = pbasis.dot( dofs )
  viscosity = 1
  inertia = model.Integral( (ubasis * u).sum(-1), domain=domain, geometry=geom, degree=5 )
  stokesres = model.Integral( viscosity * ubasis['ni,j'] * (u['i,j']+u['j,i']) - ubasis['nk,k'] * p + pbasis['n'] * u['k,k'], domain=domain, geometry=geom, degree=5 )
  residual = stokesres + model.Integral( ubasis['ni'] * u['i,j'] * u['j'], domain=domain, geometry=geom, degree=5 )
  cons = domain.boundary['top,bottom'].project( [0,0], onto=ubasis, geometry=geom, ischeme='gauss2' ) \
       | domain.boundary['left'].project( [geom[1]*(1-geom[1]),0], onto=ubasis, geometry=geom, ischeme='gauss2' )
  lhs0 = model.solve_linear( dofs, residual=stokesres, constrain=cons )

  for name in 'direct', 'newton', 'pseudotime':
    @unittest( name=name, raises=name=='direct' and model.ModelError)
    def res():
      tol = 1e-10
      if name == 'direct':
        lhs = model.solve_linear( dofs, residual=residual, constrain=cons )
      elif name == 'newton':
        lhs = model.newton( dofs, residual=residual, lhs0=lhs0, freezedofs=cons.where ).solve( tol=tol, maxiter=2 )
      else:
        lhs = model.pseudotime( dofs, residual=residual, lhs0=lhs0, freezedofs=cons.where, inertia=inertia, timestep=1 ).solve( tol=tol, maxiter=3 )
      res = residual.replace( dofs, lhs ).eval()
      resnorm = numpy.linalg.norm( res[~cons.where] )
      assert resnorm < tol
Esempio n. 2
0
 def setUp(self):
     super().setUp()
     domain, geom = mesh.rectilinear([numpy.linspace(0, 1, 9)] * 2)
     ubasis, pbasis = function.chain([
         domain.basis('std', degree=2).vector(2),
         domain.basis('std', degree=1),
     ])
     dofs = function.Argument('dofs', [len(ubasis)])
     u = ubasis.dot(dofs)
     p = pbasis.dot(dofs)
     viscosity = 1e-3
     self.inertia = domain.integral((ubasis * u).sum(-1) * function.J(geom),
                                    degree=5)
     stokesres = domain.integral(
         (viscosity * (ubasis.grad(geom) *
                       (u.grad(geom) + u.grad(geom).T)).sum([-1, -2]) -
          ubasis.div(geom) * p + pbasis * u.div(geom)) * function.J(geom),
         degree=5)
     self.residual = stokesres + domain.integral(
         (ubasis *
          (u.grad(geom) * u).sum(-1) * u).sum(-1) * function.J(geom),
         degree=5)
     self.cons = domain.boundary['top,bottom'].project([0,0], onto=ubasis, geometry=geom, ischeme='gauss4') \
               | domain.boundary['left'].project([geom[1]*(1-geom[1]),0], onto=ubasis, geometry=geom, ischeme='gauss4')
     self.lhs0 = solver.solve_linear('dofs',
                                     residual=stokesres,
                                     constrain=self.cons)
     self.tol = 1e-10
Esempio n. 3
0
def main(viscosity=1.3e-3, density=1e3, pout=223e5, uw=-0.01, nelems=10):
    # viscosity = 1.3e-3
    # density = 1e3
    # pout = 223e5
    # nelems = 10
    # uw = -0.01

    domain, geom = mesh.rectilinear([numpy.linspace(0, 1, nelems), numpy.linspace(1, 2, nelems), [0, 2 * numpy.pi]],
                                periodic=[2])
    domain = domain.withboundary(inner='bottom', outer='top')

    ns = function.Namespace()
    ns.y, ns.r, ns.θ = geom
    ns.x_i = '<r cos(θ), y, r sin(θ)>_i'
    ns.uybasis, ns.urbasis, ns.pbasis = function.chain([
    domain.basis('std', degree=3, removedofs=((0,-1), None, None)),  # remove normal component at y=0 and y=1
    domain.basis('std', degree=3, removedofs=((0,-1), None, None)),  # remove tangential component at y=0 (no slip)
    domain.basis('std', degree=2)])
    ns.ubasis_ni = '<urbasis_n cos(θ), uybasis_n, urbasis_n sin(θ)>_i'
    ns.viscosity = viscosity
    ns.density = density
    ns.u_i = 'ubasis_ni ?lhs_n'
    ns.p = 'pbasis_n ?lhs_n'
    ns.sigma_ij = 'viscosity (u_i,j + u_j,i) - p δ_ij'
    ns.pout = pout
    ns.uw = uw
    ns.uw_i = 'uw <cos(phi), 0, sin(phi)>_i'
    ns.tout_i = '-pout n_i'
    ns.uw_i = 'uw n_i'  # uniform outflow

    res = domain.integral('(viscosity ubasis_ni,j u_i,j - p ubasis_ni,i + pbasis_n u_k,k) d:x' @ ns, degree=6)
    # res -= domain[1].boundary['inner'].integral('ubasis_ni tout_i d:x' @ ns, degree=6)

    sqr = domain.boundary['inner'].integral('(u_i - uw_i) (u_i - uw_i) d:x' @ ns, degree=6)
    # sqr = domain.boundary['outer'].integral('(u_i - uin_i) (u_i - uin_i) d:x' @ ns, degree=6)
    sqr -= domain.boundary['outer'].integral('(p - pout) (p - pout) d:x' @ ns, degree=6)
    cons = solver.optimize('lhs', sqr, droptol=1e-15)

    lhs = solver.solve_linear('lhs', res, constrain=cons)

    plottopo = domain[:, :, 0:].boundary['back']

    bezier = plottopo.sample('bezier', 10)
    r, y, p, u = bezier.eval([ns.r, ns.y, ns.p, function.norm2(ns.u)], lhs=lhs)
    with export.mplfigure('pressure.png', dpi=800) as fig:
        ax = fig.add_subplot(111, title='pressure', aspect=1)
        ax.autoscale(enable=True, axis='both', tight=True)
        im = ax.tripcolor(r, y, bezier.tri, p, shading='gouraud', cmap='jet')
        ax.add_collection(
            collections.LineCollection(numpy.array([y, r]).T[bezier.hull], colors='k', linewidths=0.2, alpha=0.2))
        fig.colorbar(im)

    uniform = plottopo.sample('uniform', 1)
    r_, y_, uv = uniform.eval([ns.r, ns.y, ns.u], lhs=lhs)
    with export.mplfigure('velocity.png', dpi=800) as fig:
        ax = fig.add_subplot(111, title='Velocity', aspect=1)
        ax.autoscale(enable=True, axis='both', tight=True)
        im = ax.tripcolor(r, y, bezier.tri, u, shading='gouraud', cmap='jet')
        ax.quiver(r_, y_, uv[:, 0], uv[:, 1], angles='xy', scale_units='xy')
        fig.colorbar(im)
Esempio n. 4
0
 def setUp(self):
   super().setUp()
   domain, geom = mesh.rectilinear([numpy.linspace(0,1,9)] * 2)
   gauss = domain.sample('gauss', 5)
   uin = geom[1] * (1-geom[1])
   dx = function.J(geom, 2)
   ubasis = domain.basis('std', degree=2)
   pbasis = domain.basis('std', degree=1)
   if self.single:
     ubasis, pbasis = function.chain([ubasis.vector(2), pbasis])
     dofs = function.Argument('dofs', [len(ubasis)])
     u = ubasis.dot(dofs)
     p = pbasis.dot(dofs)
     dofs = 'dofs'
     ures = gauss.integral((self.viscosity * (ubasis.grad(geom) * (u.grad(geom) + u.grad(geom).T)).sum([-1,-2]) - ubasis.div(geom) * p) * dx)
     dres = gauss.integral((ubasis * (u.grad(geom) * u).sum(-1)).sum(-1) * dx)
   else:
     u = (ubasis[:,numpy.newaxis] * function.Argument('dofs', [len(ubasis), 2])).sum(0)
     p = (pbasis * function.Argument('pdofs', [len(pbasis)])).sum(0)
     dofs = 'dofs', 'pdofs'
     ures = gauss.integral((self.viscosity * (ubasis[:,numpy.newaxis].grad(geom) * (u.grad(geom) + u.grad(geom).T)).sum(-1) - ubasis.grad(geom) * p) * dx)
     dres = gauss.integral(ubasis[:,numpy.newaxis] * (u.grad(geom) * u).sum(-1) * dx)
   pres = gauss.integral((pbasis * u.div(geom)) * dx)
   cons = solver.optimize('dofs', domain.boundary['top,bottom'].integral((u**2).sum(), degree=4), droptol=1e-10)
   cons = solver.optimize('dofs', domain.boundary['left'].integral((u[0]-uin)**2 + u[1]**2, degree=4), droptol=1e-10, constrain=cons)
   self.cons = cons if self.single else {'dofs': cons}
   stokes = solver.solve_linear(dofs, residual=ures + pres if self.single else [ures, pres], constrain=self.cons)
   self.arguments = dict(dofs=stokes) if self.single else stokes
   self.residual = ures + dres + pres if self.single else [ures + dres, pres]
   inertia = gauss.integral(.5 * (u**2).sum(-1) * dx).derivative('dofs')
   self.inertia = inertia if self.single else [inertia, None]
   self.tol = 1e-10
   self.dofs = dofs
   self.frozen = types.frozenarray if self.single else solver.argdict
Esempio n. 5
0
    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))
Esempio n. 6
0
def main(nelems: int, degree: int, reynolds: float):
    '''
  Driven cavity benchmark problem using compatible spaces.

  .. arguments::

     nelems [12]
       Number of elements along edge.
     degree [2]
       Polynomial degree for velocity; the pressure space is one degree less.
     reynolds [1000]
       Reynolds number, taking the domain size as characteristic length.
  '''

    verts = numpy.linspace(0, 1, nelems + 1)
    domain, geom = mesh.rectilinear([verts, verts])

    ns = function.Namespace()
    ns.x = geom
    ns.Re = reynolds
    ns.uxbasis, ns.uybasis, ns.pbasis, ns.lbasis = function.chain([
        domain.basis('spline',
                     degree=(degree, degree - 1),
                     removedofs=((0, -1), None)),
        domain.basis('spline',
                     degree=(degree - 1, degree),
                     removedofs=(None, (0, -1))),
        domain.basis('spline', degree=degree - 1),
        [1],  # lagrange multiplier
    ])
    ns.ubasis_ni = '<uxbasis_n, uybasis_n>_i'
    ns.u_i = 'ubasis_ni ?lhs_n'
    ns.p = 'pbasis_n ?lhs_n'
    ns.l = 'lbasis_n ?lhs_n'
    ns.stress_ij = '(u_i,j + u_j,i) / Re - p δ_ij'
    ns.uwall = domain.boundary.indicator('top'), 0
    ns.N = 5 * degree * nelems  # nitsche constant based on element size = 1/nelems
    ns.nitsche_ni = '(N ubasis_ni - (ubasis_ni,j + ubasis_nj,i) n_j) / Re'

    res = domain.integral(
        '(ubasis_ni,j stress_ij + pbasis_n (u_k,k + l) + lbasis_n p) d:x' @ ns,
        degree=2 * degree)
    res += domain.boundary.integral(
        '(nitsche_ni (u_i - uwall_i) - ubasis_ni stress_ij n_j) d:x' @ ns,
        degree=2 * degree)
    with treelog.context('stokes'):
        lhs0 = solver.solve_linear('lhs', res)
        postprocess(domain, ns, lhs=lhs0)

    res += domain.integral('ubasis_ni u_i,j u_j d:x' @ ns, degree=3 * degree)
    with treelog.context('navierstokes'):
        lhs1 = solver.newton('lhs', res, lhs0=lhs0).solve(tol=1e-10)
        postprocess(domain, ns, lhs=lhs1)

    return lhs0, lhs1
Esempio n. 7
0
def main(nelems: int, etype: str, degree: int, reynolds: float):
    '''
  Driven cavity benchmark problem.

  .. arguments::

     nelems [12]
       Number of elements along edge.
     etype [square]
       Element type (square/triangle/mixed).
     degree [2]
       Polynomial degree for velocity; the pressure space is one degree less.
     reynolds [1000]
       Reynolds number, taking the domain size as characteristic length.
  '''

    domain, geom = mesh.unitsquare(nelems, etype)

    ns = function.Namespace()
    ns.Re = reynolds
    ns.x = geom
    ns.ubasis, ns.pbasis = function.chain([
        domain.basis('std', degree=degree).vector(2),
        domain.basis('std', degree=degree - 1),
    ])
    ns.u_i = 'ubasis_ni ?lhs_n'
    ns.p = 'pbasis_n ?lhs_n'
    ns.stress_ij = '(u_i,j + u_j,i) / Re - p δ_ij'

    sqr = domain.boundary.integral('u_k u_k d:x' @ ns, degree=degree * 2)
    wallcons = solver.optimize('lhs', sqr, droptol=1e-15)

    sqr = domain.boundary['top'].integral('(u_0 - 1)^2 d:x' @ ns,
                                          degree=degree * 2)
    lidcons = solver.optimize('lhs', sqr, droptol=1e-15)

    cons = numpy.choose(numpy.isnan(lidcons), [lidcons, wallcons])
    cons[-1] = 0  # pressure point constraint

    res = domain.integral('(ubasis_ni,j stress_ij + pbasis_n u_k,k) d:x' @ ns,
                          degree=degree * 2)
    with treelog.context('stokes'):
        lhs0 = solver.solve_linear('lhs', res, constrain=cons)
        postprocess(domain, ns, lhs=lhs0)

    res += domain.integral(
        '.5 (ubasis_ni u_i,j - ubasis_ni,j u_i) u_j d:x' @ ns,
        degree=degree * 3)
    with treelog.context('navierstokes'):
        lhs1 = solver.newton('lhs', res, lhs0=lhs0,
                             constrain=cons).solve(tol=1e-10)
        postprocess(domain, ns, lhs=lhs1)

    return lhs0, lhs1
Esempio n. 8
0
def navierstokes():
    domain, geom = mesh.rectilinear([numpy.linspace(0, 1, 9)] * 2)
    ubasis, pbasis = function.chain([
        domain.basis('std', degree=2).vector(2),
        domain.basis('std', degree=1),
    ])
    dofs = function.DerivativeTarget([len(ubasis)])
    u = ubasis.dot(dofs)
    p = pbasis.dot(dofs)
    viscosity = 1
    inertia = model.Integral((ubasis * u).sum(-1),
                             domain=domain,
                             geometry=geom,
                             degree=5)
    stokesres = model.Integral(viscosity * ubasis['ni,j'] *
                               (u['i,j'] + u['j,i']) - ubasis['nk,k'] * p +
                               pbasis['n'] * u['k,k'],
                               domain=domain,
                               geometry=geom,
                               degree=5)
    residual = stokesres + model.Integral(ubasis['ni'] * u['i,j'] * u['j'],
                                          domain=domain,
                                          geometry=geom,
                                          degree=5)
    cons = domain.boundary['top,bottom'].project( [0,0], onto=ubasis, geometry=geom, ischeme='gauss2' ) \
         | domain.boundary['left'].project( [geom[1]*(1-geom[1]),0], onto=ubasis, geometry=geom, ischeme='gauss2' )
    lhs0 = model.solve_linear(dofs, residual=stokesres, constrain=cons)

    for name in 'direct', 'newton', 'pseudotime':

        @unittest(name=name, raises=name == 'direct' and model.ModelError)
        def res():
            tol = 1e-10
            if name == 'direct':
                lhs = model.solve_linear(dofs,
                                         residual=residual,
                                         constrain=cons)
            elif name == 'newton':
                lhs = model.newton(dofs,
                                   residual=residual,
                                   lhs0=lhs0,
                                   freezedofs=cons.where).solve(tol=tol,
                                                                maxiter=2)
            else:
                lhs = model.pseudotime(dofs,
                                       residual=residual,
                                       lhs0=lhs0,
                                       freezedofs=cons.where,
                                       inertia=inertia,
                                       timestep=1).solve(tol=tol, maxiter=3)
            res = residual.replace(dofs, lhs).eval()
            resnorm = numpy.linalg.norm(res[~cons.where])
            assert resnorm < tol
Esempio n. 9
0
 def setUp(self):
   super().setUp()
   domain, geom = mesh.rectilinear([numpy.linspace(0,1,9)] * 2)
   ubasis, pbasis = function.chain([
     domain.basis('std', degree=2).vector(2),
     domain.basis('std', degree=1),
   ])
   dofs = function.Argument('dofs', [len(ubasis)])
   u = ubasis.dot(dofs)
   p = pbasis.dot(dofs)
   viscosity = 1
   self.inertia = domain.integral((ubasis * u).sum(-1), geometry=geom, degree=5)
   stokesres = domain.integral(viscosity * (ubasis.grad(geom) * (u.grad(geom)+u.grad(geom).T)).sum([-1,-2]) - ubasis.div(geom) * p + pbasis * u.div(geom), geometry=geom, degree=5)
   self.residual = stokesres + domain.integral((ubasis * (u.grad(geom) * u).sum(-1) * u).sum(-1), geometry=geom, degree=5)
   self.cons = domain.boundary['top,bottom'].project([0,0], onto=ubasis, geometry=geom, ischeme='gauss2') \
             | domain.boundary['left'].project([geom[1]*(1-geom[1]),0], onto=ubasis, geometry=geom, ischeme='gauss2')
   self.lhs0 = solver.solve_linear('dofs', residual=stokesres, constrain=self.cons)
   self.tol = 1e-10
Esempio n. 10
0
def main(
    uinf      = 2.0,
    L         = 2.0,
    R         = 0.5,
    nelems    = 7  ,
    degree    = 2  ,
    maxrefine = 3  ,
    withplots = True
    ):
  
  """`main` functions with different parameters.
  
  Parameters
  ----------
  uinf : float 
      free stream velocity
  L : float
      domain size
  R : float
      cylinder radius
  nelems : int
      number of elements
  degree : int
      b-spline degree
  maxrefine : int
      bisectioning steps
  withplots : bool
      create plots
  Returns
  -------
  lhs : float
      solution φ
  err : float
      L2 norm, H1 norm and energy errors
  """
  # construct mesh
  verts = numpy.linspace(-L/2, L/2, nelems+1)
  domain, geom = mesh.rectilinear([verts, verts])
  # trim out a circle
  domain = domain.trim(function.norm2(geom)-R, maxrefine=maxrefine)
  # initialize namespace
  ns = function.Namespace()
  ns.R    = R
  ns.x    = geom
  ns.uinf = uinf
  # construct function space and lagrange multiplier
  ns.phibasis, ns.lbasis = function.chain([domain.basis('spline', degree=degree),[1.]])
  ns.phi = ' phibasis_n ?lhs_n'
  ns.u   = 'sqrt( (phibasis_n,i ?lhs_n) (phibasis_m,i ?lhs_m) )'
  ns.l   = 'lbasis_n ?lhs_n'
  # set the exact solution
  ns.phiexact = 'uinf x_1 ( 1 - R^2 / (x_0^2 + x_1^2) )' # average is zero
  ns.phierror = 'phi - phiexact'
  # construct residual
  res  = domain.integral('-phibasis_n,i phi_,i' @ ns, geometry=ns.x, degree=degree*2)
  res += domain.boundary.integral('phibasis_n phiexact_,i n_i' @ ns, geometry=ns.x, degree=degree*2)
  res += domain.integral('lbasis_n phi + l phibasis_n' @ ns, geometry=ns.x, degree=degree*2)
  # find lhs such that res == 0 and substitute this lhs in the namespace
  lhs = solver.solve_linear('lhs', res)
  ns = ns(lhs=lhs)
  # evaluate error
  err1 = numpy.sqrt(domain.integrate(['phierror phierror' @ns,'phierror phierror + phierror_,i phierror_,i' @ns, '0.5 phi_,i phi_,i' @ns], geometry=ns.x, degree=degree*4))
  err2 = abs(err1[2] - 2.7710377946088443)
  err = numpy.array([err1[0],err1[1],err2])
  log.info('errors: L2={:.2e}, H1={:.2e}, eh={:.2e}'.format(*err))
  if withplots:
    makeplots(domain, ns)
  return lhs, err
Esempio n. 11
0
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
Esempio n. 12
0
    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]]),
        )
Esempio n. 13
0
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,
    degree: 'polynomial degree' = 2,
    withplots: 'create plots' = True,
  ):

  log.user('reynolds number: {:.1f}'.format(density / viscosity)) # based on unit length and velocity

  # create namespace
  ns = function.Namespace()
  ns.uinf = 1, 0
  ns.density = density
  ns.viscosity = 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, x0 = mesh.rectilinear([range(melems+1),numpy.linspace(0,2*numpy.pi,2*nelems+1)], periodic=(1,))
  rho, phi = x0
  phi += 1e-3 # tiny nudge (0.057 deg) to break element symmetry
  radius = .5 * function.exp(rscale * rho)
  ns.x = radius * function.trigtangent(phi)
  domain = domain.withboundary(inner='left', inflow=domain.boundary['right'].select(-ns.uinf.dotnorm(ns.x), ischeme='gauss1'))

  # prepare bases (using piola transformation to maintain u/p compatibility)
  J = ns.x.grad(x0)
  detJ = function.determinant(J)
  ns.unbasis, ns.utbasis, ns.pbasis = function.chain([ # compatible spaces using piola transformation
    domain.basis('spline', degree=(degree+1,degree), removedofs=((0,),None))[:,_] * J[:,0] / detJ,
    domain.basis('spline', degree=(degree,degree+1))[:,_] * J[:,1] / detJ,
    domain.basis('spline', degree=degree) / detJ,
  ])
  ns.ubasis_ni = 'unbasis_ni + utbasis_ni'

  # populate namespace
  ns.u_i = 'ubasis_ni ?lhs_n'
  ns.p = 'pbasis_n ?lhs_n'
  ns.sigma_ij = 'viscosity (u_i,j + u_j,i) - p δ_ij'
  ns.hinner = 2 * numpy.pi / nelems
  ns.c = 5 * (degree+1) / ns.hinner
  ns.nietzsche_ni = 'viscosity (c ubasis_ni - (ubasis_ni,j + ubasis_nj,i) n_j)'
  ns.ucyl = -.5 * rotation * function.trignormal(phi)

  # create residual vector components
  res = domain.integral('ubasis_ni,j sigma_ij + pbasis_n u_k,k' @ ns, geometry=ns.x, degree=2*(degree+1))
  res += domain.boundary['inner'].integral('nietzsche_ni (u_i - ucyl_i)' @ ns, geometry=ns.x, degree=2*(degree+1))
  oseen = domain.integral('density ubasis_ni u_i,j uinf_j' @ ns, geometry=ns.x, degree=2*(degree+1))
  convec = domain.integral('density ubasis_ni u_i,j u_j' @ ns, geometry=ns.x, degree=3*(degree+1))
  inertia = domain.integral('density ubasis_ni u_i' @ ns, geometry=ns.x, degree=2*(degree+1))

  # constrain full velocity vector at inflow
  sqr = domain.boundary['inflow'].integral('(u_i - uinf_i) (u_i - uinf_i)' @ ns, geometry=ns.x, degree=9)
  cons = solver.optimize('lhs', sqr, droptol=1e-15)

  # solve unsteady navier-stokes equations, starting from stationary oseen flow
  lhs0 = solver.solve_linear('lhs', res+oseen, constrain=cons)
  makeplots = MakePlots(domain, ns, timestep=timestep, rotation=rotation) if withplots else lambda *args: None
  for istep, lhs in log.enumerate('timestep', solver.impliciteuler('lhs', residual=res+convec, inertia=inertia, lhs0=lhs0, timestep=timestep, constrain=cons, newtontol=1e-10)):
    makeplots(lhs)
    if istep * timestep >= tmax:
      break

  return lhs0, lhs
Esempio n. 14
0
        rmul[0] = 3
        rmul[-1] = 3
        pmul = [2] * len(pkts)

        thbasis = case.domain.basis(
            'spline',
            degree=(2, 2),
            knotvalues=[rkts, pkts],
            knotmultiplicities=[rmul, pmul],
        )
        bases = [
            thbasis[:, _] * (1, 0), thbasis[:, _] * (0, 1),
            case.domain.basis('spline', degree=1)
        ]

    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, len(pbasis)


def mk_lift(case):
    mu = case.parameter()
    vbasis = case.basis('v', mu)

    geom = case['geometry'](mu)
    x, __ = geom
    cons = case.domain.boundary['left'].project((0, 0),
Esempio n. 15
0
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,
    degree: 'polynomial degree' = 2,
    figures: 'create figures' = True,
  ):

  log.user('reynolds number: {:.1f}'.format(density / viscosity)) # based on unit length and velocity

  # create namespace
  ns = function.Namespace()
  ns.uinf = 1, 0
  ns.density = density
  ns.viscosity = 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, x0 = mesh.rectilinear([range(melems+1),numpy.linspace(0,2*numpy.pi,2*nelems+1)], periodic=(1,))
  rho, phi = x0
  phi += 1e-3 # tiny nudge (0.057 deg) to break element symmetry
  radius = .5 * function.exp(rscale * rho)
  ns.x = radius * function.trigtangent(phi)
  domain = domain.withboundary(inner='left', inflow=domain.boundary['right'].select(-ns.uinf.dotnorm(ns.x), ischeme='gauss1'))

  # prepare bases (using piola transformation to maintain u/p compatibility)
  J = ns.x.grad(x0)
  detJ = function.determinant(J)
  ns.unbasis, ns.utbasis, ns.pbasis = function.chain([ # compatible spaces using piola transformation
    domain.basis('spline', degree=(degree+1,degree), removedofs=((0,),None))[:,_] * J[:,0] / detJ,
    domain.basis('spline', degree=(degree,degree+1))[:,_] * J[:,1] / detJ,
    domain.basis('spline', degree=degree) / detJ,
  ])
  ns.ubasis_ni = 'unbasis_ni + utbasis_ni'

  # populate namespace
  ns.u_i = 'ubasis_ni ?lhs_n'
  ns.p = 'pbasis_n ?lhs_n'
  ns.sigma_ij = 'viscosity (u_i,j + u_j,i) - p δ_ij'
  ns.hinner = 2 * numpy.pi / nelems
  ns.c = 5 * (degree+1) / ns.hinner
  ns.nietzsche_ni = 'viscosity (c ubasis_ni - (ubasis_ni,j + ubasis_nj,i) n_j)'
  ns.ucyl = -.5 * rotation * function.trignormal(phi)

  # create residual vector components
  res = domain.integral('ubasis_ni,j sigma_ij + pbasis_n u_k,k' @ ns, geometry=ns.x, degree=2*(degree+1))
  res += domain.boundary['inner'].integral('nietzsche_ni (u_i - ucyl_i)' @ ns, geometry=ns.x, degree=2*(degree+1))
  oseen = domain.integral('density ubasis_ni u_i,j uinf_j' @ ns, geometry=ns.x, degree=2*(degree+1))
  convec = domain.integral('density ubasis_ni u_i,j u_j' @ ns, geometry=ns.x, degree=3*(degree+1))
  inertia = domain.integral('density ubasis_ni u_i' @ ns, geometry=ns.x, degree=2*(degree+1))

  # constrain full velocity vector at inflow
  sqr = domain.boundary['inflow'].integral('(u_i - uinf_i) (u_i - uinf_i)' @ ns, geometry=ns.x, degree=9)
  cons = solver.optimize('lhs', sqr, droptol=1e-15)

  # solve unsteady navier-stokes equations, starting from stationary oseen flow
  lhs0 = solver.solve_linear('lhs', res+oseen, constrain=cons)
  makeplots = MakePlots(domain, ns, timestep=timestep, rotation=rotation) if figures else lambda *args: None
  for istep, lhs in log.enumerate('timestep', solver.impliciteuler('lhs', residual=res+convec, inertia=inertia, lhs0=lhs0, timestep=timestep, constrain=cons, newtontol=1e-10)):
    makeplots(lhs)
    if istep * timestep >= tmax:
      break

  return lhs0, lhs
Esempio n. 16
0
    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()
Esempio n. 17
0
    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()
Esempio n. 18
0
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',
    figures: 'create figures' = True,
):

    mineps = 1. / nelems
    if not epsilon:
        log.info('setting epsilon={}'.format(mineps))
        epsilon = mineps
    elif epsilon < mineps:
        log.warning('epsilon under crititical threshold: {} < {}'.format(
            epsilon, mineps))

    # create namespace
    ns = function.Namespace()
    ns.epsilon = epsilon
    ns.ewall = .5 * numpy.cos(theta * numpy.pi / 180)

    # construct mesh
    xnodes = ynodes = numpy.linspace(0, 1, nelems + 1)
    domain, ns.x = mesh.rectilinear([xnodes, ynodes])

    # prepare bases
    ns.cbasis, ns.mubasis = function.chain(
        [domain.basis('spline', degree=2),
         domain.basis('spline', degree=2)])

    # polulate namespace
    ns.c = 'cbasis_n ?lhs_n'
    ns.c0 = 'cbasis_n ?lhs0_n'
    ns.mu = 'mubasis_n ?lhs_n'
    ns.f = '(6 c0 - 2 c0^3 - 4 c) / epsilon^2'

    # construct initial condition
    if init == 'random':
        numpy.random.seed(0)
        lhs0 = numpy.random.normal(0, .5, ns.cbasis.shape)
    elif init == 'bubbles':
        R1 = .25
        R2 = numpy.sqrt(.5) * R1  # area2 = .5 * area1
        ns.cbubble1 = function.tanh(
            (R1 - function.norm2(ns.x -
                                 (.5 + R2 / numpy.sqrt(2) + .8 * ns.epsilon)))
            / ns.epsilon)
        ns.cbubble2 = function.tanh(
            (R2 - function.norm2(ns.x -
                                 (.5 - R1 / numpy.sqrt(2) - .8 * ns.epsilon)))
            / ns.epsilon)
        sqr = domain.integral('(c - cbubble1 - cbubble2 - 1)^2 + mu^2' @ ns,
                              geometry=ns.x,
                              degree=4)
        lhs0 = solver.optimize('lhs', sqr)
    else:
        raise Exception('unknown init %r' % init)

    # construct residual
    res = domain.integral(
        'epsilon^2 cbasis_n,k mu_,k + mubasis_n (mu + f) - mubasis_n,k c_,k'
        @ ns,
        geometry=ns.x,
        degree=4)
    res -= domain.boundary.integral('mubasis_n ewall' @ ns,
                                    geometry=ns.x,
                                    degree=4)
    inertia = domain.integral('cbasis_n c' @ ns, geometry=ns.x, degree=4)

    # solve time dependent problem
    nsteps = numeric.round(maxtime / timestep)
    makeplots = MakePlots(domain, nsteps,
                          ns) if figures else lambda *args: None
    for istep, lhs in log.enumerate(
            'timestep',
            solver.impliciteuler('lhs',
                                 target0='lhs0',
                                 residual=res,
                                 inertia=inertia,
                                 timestep=timestep,
                                 lhs0=lhs0)):
        makeplots(lhs)
        if istep == nsteps:
            break

    return lhs0, lhs
Esempio n. 19
0
    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))
Esempio n. 20
0
def main(inflow: 'inflow velocity' = 10,
         viscosity: 'kinematic viscosity' = 1.0,
         density: 'density' = 1.0,
         theta=0.5,
         timestepsize=0.01):

    # mesh and geometry definition
    grid_x_1 = numpy.linspace(-3, -1, 7)
    grid_x_1 = grid_x_1[:-1]
    grid_x_2 = numpy.linspace(-1, -0.3, 8)
    grid_x_2 = grid_x_2[:-1]
    grid_x_3 = numpy.linspace(-0.3, 0.3, 13)
    grid_x_3 = grid_x_3[:-1]
    grid_x_4 = numpy.linspace(0.3, 1, 8)
    grid_x_4 = grid_x_4[:-1]
    grid_x_5 = numpy.linspace(1, 3, 7)
    grid_x = numpy.concatenate(
        (grid_x_1, grid_x_2, grid_x_3, grid_x_4, grid_x_5), axis=None)
    grid_y_1 = numpy.linspace(0, 1.5, 16)
    grid_y_1 = grid_y_1[:-1]
    grid_y_2 = numpy.linspace(1.5, 2, 4)
    grid_y_2 = grid_y_2[:-1]
    grid_y_3 = numpy.linspace(2, 4, 7)
    grid_y = numpy.concatenate((grid_y_1, grid_y_2, grid_y_3), axis=None)
    grid = [grid_x, grid_y]

    topo, geom = mesh.rectilinear(grid)
    domain = topo.withboundary(inflow='left', wall='top,bottom', outflow='right') - \
        topo[18:20, :10].withboundary(flap='left,right,top')

    # Nutils namespace
    ns = function.Namespace()

    # time approximations
    # TR interpolation
    ns._functions['t'] = lambda f: theta * f + (1 - theta) * subs0(f)
    ns._functions_nargs['t'] = 1
    # 1st order FD
    ns._functions['δt'] = lambda f: (f - subs0(f)) / dt
    ns._functions_nargs['δt'] = 1
    # 2nd order FD
    ns._functions['tt'] = lambda f: (1.5 * f - 2 * subs0(f) + 0.5 * subs00(f)
                                     ) / dt
    ns._functions_nargs['tt'] = 1
    # extrapolation for pressure
    ns._functions['tp'] = lambda f: (1.5 * f - 0.5 * subs0(f))
    ns._functions_nargs['tp'] = 1

    ns.nu = viscosity
    ns.rho = density
    ns.uin = inflow
    ns.x0 = geom  # reference geometry
    ns.dbasis = domain.basis('std', degree=1).vector(2)
    ns.d_i = 'dbasis_ni ?meshdofs_n'
    ns.umesh_i = 'dbasis_ni (1.5 ?meshdofs_n - 2 ?oldmeshdofs_n + 0.5 ?oldoldmeshdofs_n ) / ?dt'
    ns.x_i = 'x0_i + d_i'  # moving geometry
    ns.ubasis, ns.pbasis = function.chain([
        domain.basis('std', degree=2).vector(2),
        domain.basis('std', degree=1),
    ])
    ns.F_i = 'ubasis_ni ?F_n'  # stress field
    ns.urel_i = 'ubasis_ni ?lhs_n'  # relative velocity
    ns.u_i = 'umesh_i + urel_i'  # total velocity
    ns.p = 'pbasis_n ?lhs_n'  # pressure

    # initialization of dofs
    meshdofs = numpy.zeros(len(ns.dbasis))
    oldmeshdofs = meshdofs
    oldoldmeshdofs = meshdofs
    oldoldoldmeshdofs = meshdofs
    lhs0 = numpy.zeros(len(ns.ubasis))

    # for visualization
    bezier = domain.sample('bezier', 2)

    # preCICE setup
    configFileName = "../precice-config.xml"
    participantName = "Fluid"
    solverProcessIndex = 0
    solverProcessSize = 1
    interface = precice.Interface(participantName, configFileName,
                                  solverProcessIndex, solverProcessSize)

    # define coupling meshes
    meshName = "Fluid-Mesh"
    meshID = interface.get_mesh_id(meshName)

    couplinginterface = domain.boundary['flap']
    couplingsample = couplinginterface.sample(
        'gauss', degree=2)  # mesh located at Gauss points
    dataIndices = interface.set_mesh_vertices(meshID,
                                              couplingsample.eval(ns.x0))

    # coupling data
    writeData = "Force"
    readData = "Displacement"
    writedataID = interface.get_data_id(writeData, meshID)
    readdataID = interface.get_data_id(readData, meshID)

    # initialize preCICE
    precice_dt = interface.initialize()
    dt = min(precice_dt, timestepsize)

    # boundary conditions for fluid equations
    sqr = domain.boundary['wall,flap'].integral('urel_k urel_k d:x0' @ ns,
                                                degree=4)
    cons = solver.optimize('lhs', sqr, droptol=1e-15)
    sqr = domain.boundary['inflow'].integral(
        '((urel_0 - uin)^2 + urel_1^2) d:x0' @ ns, degree=4)
    cons = solver.optimize('lhs', sqr, droptol=1e-15, constrain=cons)

    # weak form fluid equations
    res = domain.integral('t(ubasis_ni,j (u_i,j + u_j,i) rho nu d:x)' @ ns,
                          degree=4)
    res += domain.integral('(-ubasis_ni,j p δ_ij + pbasis_n u_k,k) d:x' @ ns,
                           degree=4)
    res += domain.integral('rho ubasis_ni δt(u_i d:x)' @ ns, degree=4)
    res += domain.integral('rho ubasis_ni t(u_i,j urel_j d:x)' @ ns, degree=4)

    # weak form for force computation
    resF = domain.integral('(ubasis_ni,j (u_i,j + u_j,i) rho nu d:x)' @ ns,
                           degree=4)
    resF += domain.integral('tp(-ubasis_ni,j p δ_ij d:x)' @ ns, degree=4)
    resF += domain.integral('pbasis_n u_k,k d:x' @ ns, degree=4)
    resF += domain.integral('rho ubasis_ni tt(u_i d:x)' @ ns, degree=4)
    resF += domain.integral('rho ubasis_ni (u_i,j urel_j d:x)' @ ns, degree=4)
    resF += couplinginterface.sample('gauss',
                                     4).integral('ubasis_ni F_i d:x' @ ns)
    consF = numpy.isnan(
        solver.optimize('F',
                        couplinginterface.sample('gauss',
                                                 4).integral('F_i F_i' @ ns),
                        droptol=1e-10))

    # boundary conditions mesh displacements
    sqr = domain.boundary['inflow,outflow,wall'].integral('d_i d_i' @ ns,
                                                          degree=2)
    meshcons0 = solver.optimize('meshdofs', sqr, droptol=1e-15)

    # weak form mesh displacements
    meshsqr = domain.integral('d_i,x0_j d_i,x0_j d:x0' @ ns, degree=2)

    # better initial guess: start from Stokes solution, comment out for comparison with other solvers
    #res_stokes = domain.integral('(ubasis_ni,j ((u_i,j + u_j,i) rho nu - p δ_ij) + pbasis_n u_k,k) d:x' @ ns, degree=4)
    #lhs0 = solver.solve_linear('lhs', res_stokes, constrain=cons, arguments=dict(meshdofs=meshdofs, oldmeshdofs=oldmeshdofs, oldoldmeshdofs=oldoldmeshdofs, oldoldoldmeshdofs=oldoldoldmeshdofs, dt=dt))
    lhs00 = lhs0

    timestep = 0
    t = 0

    while interface.is_coupling_ongoing():

        # read displacements from interface
        if interface.is_read_data_available():
            readdata = interface.read_block_vector_data(
                readdataID, dataIndices)
            coupledata = couplingsample.asfunction(readdata)
            sqr = couplingsample.integral(((ns.d - coupledata)**2).sum(0))
            meshcons = solver.optimize('meshdofs',
                                       sqr,
                                       droptol=1e-15,
                                       constrain=meshcons0)
            meshdofs = solver.optimize('meshdofs', meshsqr, constrain=meshcons)

        # save checkpoint
        if interface.is_action_required(
                precice.action_write_iteration_checkpoint()):
            lhs_checkpoint = lhs0
            lhs00_checkpoint = lhs00
            t_checkpoint = t
            timestep_checkpoint = timestep
            oldmeshdofs_checkpoint = oldmeshdofs
            oldoldmeshdofs_checkpoint = oldoldmeshdofs
            oldoldoldmeshdofs_checkpoint = oldoldoldmeshdofs
            interface.mark_action_fulfilled(
                precice.action_write_iteration_checkpoint())

        # solve fluid equations
        lhs1 = solver.newton(
            'lhs',
            res,
            lhs0=lhs0,
            constrain=cons,
            arguments=dict(
                lhs0=lhs0,
                dt=dt,
                meshdofs=meshdofs,
                oldmeshdofs=oldmeshdofs,
                oldoldmeshdofs=oldoldmeshdofs,
                oldoldoldmeshdofs=oldoldoldmeshdofs)).solve(tol=1e-6)

        # write forces to interface
        if interface.is_write_data_required(dt):
            F = solver.solve_linear('F',
                                    resF,
                                    constrain=consF,
                                    arguments=dict(
                                        lhs00=lhs00,
                                        lhs0=lhs0,
                                        lhs=lhs1,
                                        dt=dt,
                                        meshdofs=meshdofs,
                                        oldmeshdofs=oldmeshdofs,
                                        oldoldmeshdofs=oldoldmeshdofs,
                                        oldoldoldmeshdofs=oldoldoldmeshdofs))
            # writedata = couplingsample.eval(ns.F, F=F) # for stresses
            writedata = couplingsample.eval('F_i d:x' @ ns, F=F, meshdofs=meshdofs) * \
                numpy.concatenate([p.weights for p in couplingsample.points])[:, numpy.newaxis]
            interface.write_block_vector_data(writedataID, dataIndices,
                                              writedata)

        # do the coupling
        precice_dt = interface.advance(dt)
        dt = min(precice_dt, timestepsize)

        # advance variables
        timestep += 1
        t += dt
        lhs00 = lhs0
        lhs0 = lhs1
        oldoldoldmeshdofs = oldoldmeshdofs
        oldoldmeshdofs = oldmeshdofs
        oldmeshdofs = meshdofs

        # read checkpoint if required
        if interface.is_action_required(
                precice.action_read_iteration_checkpoint()):
            lhs0 = lhs_checkpoint
            lhs00 = lhs00_checkpoint
            t = t_checkpoint
            timestep = timestep_checkpoint
            oldmeshdofs = oldmeshdofs_checkpoint
            oldoldmeshdofs = oldoldmeshdofs_checkpoint
            oldoldoldmeshdofs = oldoldoldmeshdofs_checkpoint
            interface.mark_action_fulfilled(
                precice.action_read_iteration_checkpoint())

        if interface.is_time_window_complete():
            x, u, p = bezier.eval(['x_i', 'u_i', 'p'] @ ns,
                                  lhs=lhs1,
                                  meshdofs=meshdofs,
                                  oldmeshdofs=oldmeshdofs,
                                  oldoldmeshdofs=oldoldmeshdofs,
                                  oldoldoldmeshdofs=oldoldoldmeshdofs,
                                  dt=dt)
            with treelog.add(treelog.DataLog()):
                export.vtk('Fluid_' + str(timestep), bezier.tri, x, u=u, p=p)

    interface.finalize()
Esempio n. 21
0
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
Esempio n. 22
0
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
Esempio n. 23
0
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
Esempio n. 24
0
def main(nelems: int, degree: int, reynolds: float, rotation: float,
         timestep: float, maxradius: float, seed: int, endtime: float):
    '''
  Flow around a cylinder.

  .. arguments::

     nelems [24]
       Element size expressed in number of elements along the cylinder wall.
       All elements have similar shape with approximately unit aspect ratio,
       with elements away from the cylinder wall growing exponentially.
     degree [3]
       Polynomial degree for velocity space; the pressure space is one degree
       less.
     reynolds [1000]
       Reynolds number, taking the cylinder radius as characteristic length.
     rotation [0]
       Cylinder rotation speed.
     timestep [.04]
       Time step
     maxradius [25]
       Target exterior radius; the actual domain size is subject to integer
       multiples of the configured element size.
     seed [0]
       Random seed for small velocity noise in the intial condition.
     endtime [inf]
       Stopping time.
  '''

    elemangle = 2 * numpy.pi / nelems
    melems = int(numpy.log(2 * maxradius) / elemangle + .5)
    treelog.info('creating {}x{} mesh, outer radius {:.2f}'.format(
        melems, nelems, .5 * numpy.exp(elemangle * melems)))
    domain, geom = mesh.rectilinear([melems, nelems], periodic=(1, ))
    domain = domain.withboundary(inner='left', outer='right')

    ns = function.Namespace()
    ns.uinf = 1, 0
    ns.r = .5 * function.exp(elemangle * geom[0])
    ns.Re = reynolds
    ns.phi = geom[1] * elemangle  # add small angle to break element symmetry
    ns.x_i = 'r <cos(phi), sin(phi)>_i'
    ns.J = ns.x.grad(geom)
    ns.unbasis, ns.utbasis, ns.pbasis = function.chain([  # compatible spaces
        domain.basis(
            'spline', degree=(degree, degree - 1), removedofs=((0, ), None)),
        domain.basis('spline', degree=(degree - 1, degree)),
        domain.basis('spline', degree=degree - 1),
    ]) / function.determinant(ns.J)
    ns.ubasis_ni = 'unbasis_n J_i0 + utbasis_n J_i1'  # piola transformation
    ns.u_i = 'ubasis_ni ?lhs_n'
    ns.p = 'pbasis_n ?lhs_n'
    ns.sigma_ij = '(u_i,j + u_j,i) / Re - p δ_ij'
    ns.h = .5 * elemangle
    ns.N = 5 * degree / ns.h
    ns.rotation = rotation
    ns.uwall_i = '0.5 rotation <-sin(phi), cos(phi)>_i'

    inflow = domain.boundary['outer'].select(
        -ns.uinf.dotnorm(ns.x),
        ischeme='gauss1')  # upstream half of the exterior boundary
    sqr = inflow.integral('(u_i - uinf_i) (u_i - uinf_i)' @ ns,
                          degree=degree * 2)
    cons = solver.optimize(
        'lhs', sqr, droptol=1e-15)  # constrain inflow semicircle to uinf

    sqr = domain.integral('(u_i - uinf_i) (u_i - uinf_i) + p^2' @ ns,
                          degree=degree * 2)
    lhs0 = solver.optimize('lhs', sqr)  # set initial condition to u=uinf, p=0

    numpy.random.seed(seed)
    lhs0 *= numpy.random.normal(1, .1, lhs0.shape)  # add small velocity noise

    res = domain.integral(
        '(ubasis_ni u_i,j u_j + ubasis_ni,j sigma_ij + pbasis_n u_k,k) d:x'
        @ ns,
        degree=9)
    res += domain.boundary['inner'].integral(
        '(N ubasis_ni - (ubasis_ni,j + ubasis_nj,i) n_j) (u_i - uwall_i) d:x / Re'
        @ ns,
        degree=9)
    inertia = domain.integral('ubasis_ni u_i d:x' @ ns, degree=9)

    bbox = numpy.array(
        [[-2, 46 / 9],
         [-2, 2]])  # bounding box for figure based on 16x9 aspect ratio
    bezier0 = domain.sample('bezier', 5)
    bezier = bezier0.subset((bezier0.eval(
        (ns.x - bbox[:, 0]) * (bbox[:, 1] - ns.x)) > 0).all(axis=1))
    interpolate = util.tri_interpolator(
        bezier.tri, bezier.eval(ns.x),
        mergetol=1e-5)  # interpolator for quivers
    spacing = .05  # initial quiver spacing
    xgrd = util.regularize(bbox, spacing)

    with treelog.iter.plain(
            'timestep',
            solver.impliciteuler('lhs',
                                 residual=res,
                                 inertia=inertia,
                                 lhs0=lhs0,
                                 timestep=timestep,
                                 constrain=cons,
                                 newtontol=1e-10)) as steps:
        for istep, lhs in enumerate(steps):

            t = istep * timestep
            x, u, normu, p = bezier.eval(
                ['x_i', 'u_i', 'sqrt(u_k u_k)', 'p'] @ ns, lhs=lhs)
            ugrd = interpolate[xgrd](u)

            with export.mplfigure('flow.png', figsize=(12.8, 7.2)) as fig:
                ax = fig.add_axes([0, 0, 1, 1],
                                  yticks=[],
                                  xticks=[],
                                  frame_on=False,
                                  xlim=bbox[0],
                                  ylim=bbox[1])
                im = ax.tripcolor(x[:, 0],
                                  x[:, 1],
                                  bezier.tri,
                                  p,
                                  shading='gouraud',
                                  cmap='jet')
                import matplotlib.collections
                ax.add_collection(
                    matplotlib.collections.LineCollection(x[bezier.hull],
                                                          colors='k',
                                                          linewidths=.1,
                                                          alpha=.5))
                ax.quiver(xgrd[:, 0],
                          xgrd[:, 1],
                          ugrd[:, 0],
                          ugrd[:, 1],
                          angles='xy',
                          width=1e-3,
                          headwidth=3e3,
                          headlength=5e3,
                          headaxislength=2e3,
                          zorder=9,
                          alpha=.5)
                ax.plot(0,
                        0,
                        'k',
                        marker=(3, 2, t * rotation * 180 / numpy.pi - 90),
                        markersize=20)
                cax = fig.add_axes([0.9, 0.1, 0.01, 0.8])
                cax.tick_params(labelsize='large')
                fig.colorbar(im, cax=cax)

            if t >= endtime:
                break

            xgrd = util.regularize(bbox, spacing, xgrd + ugrd * timestep)

    return lhs0, lhs
Esempio n. 25
0
def get_case(refine: int, degree: int):
    nel_up = int(10 * refine)
    nel_length = int(100 * refine)

    up_edges = [(0, 1), (3, 4), (6, 7), (0, 3), (1, 4), (2, 3), (5, 6)]
    length_edges = [(2, 5), (3, 6), (4, 7)]
    all_edges = [*up_edges, *length_edges]

    domain, refgeom = mesh.multipatch(
        patches=[[(0, 1), (3, 4)], [(3, 4), (6, 7)], [(2, 3), (5, 6)]],
        nelems={
            **{e: nel_up
               for e in up_edges},
            **{e: nel_length
               for e in length_edges}
        },
        patchverts=[(-1, 0), (-1, 1), (0, -1), (0, 0), (0, 1), (1, -1), (1, 0),
                    (1, 1)])

    case = NutilsCase('Backward-facing step channel', domain, refgeom, refgeom)

    NU = 1 / case.parameters.add('viscosity', 20, 50)
    L = case.parameters.add('length', 9, 12, 10)
    H = case.parameters.add('height', 0.3, 2, 1)
    V = case.parameters.add('velocity', 0.5, 1.2, 1)

    vxbasis = domain.basis('spline', degree=degree)
    vybasis = domain.basis('spline', degree=degree)
    pbasis = domain.basis('spline', degree=degree - 1)

    vdofs = len(vxbasis) + len(vybasis)
    pdofs = len(pbasis)
    ndofs = vdofs + pdofs

    vxbasis, vybasis, pbasis = fn.chain([vxbasis, vybasis, pbasis])
    vbasis = vxbasis[:, _] * (1, 0) + vybasis[:, _] * (0, 1)

    case.bases.add('v', vbasis, length=vdofs)
    case.bases.add('p', pbasis, length=pdofs)

    case['geometry'] = MuLambda(
        partial(geometry, L=L, H=H, refgeom=refgeom),
        (2, ),
        ('length', 'height'),
    )

    case.constrain(
        'v',
        'patch0-bottom',
        'patch0-top',
        'patch0-left',
        'patch1-top',
        'patch2-bottom',
        'patch2-left',
    )

    case['divergence'] = ntl.NSDivergence(ndofs, 'length', 'height')
    case['convection'] = ntl.NSConvection(ndofs, 'length', 'height')
    case['laplacian'] = ntl.Laplacian(ndofs, 'v', 'length', 'height', scale=NU)
    case['v-h1s'] = ntl.Laplacian(ndofs, 'v', 'length', 'height')
    case['p-l2'] = ntl.Mass(ndofs, 'p', 'length', 'height')

    with matrix.Scipy():
        __, y = refgeom
        profile = fn.max(0, y * (1 - y))[_] * (1, 0)
        case['lift'] = MuConstant(case.project_lift(profile, 'v'), scale=V)

        mu = case.parameter()
        lhs = solvers.stokes(case, mu)
        case['lift'] = MuConstant(case.solution_vector(lhs, mu, lift=True),
                                  scale=V)

    return case
Esempio n. 26
0
    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()
Esempio n. 27
0
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={}'.format(mineps))
    epsilon = mineps
  elif epsilon < mineps:
    log.warning('epsilon under crititical threshold: {} < {}'.format(epsilon, mineps))

  # create namespace
  ns = function.Namespace()
  ns.epsilon = epsilon
  ns.ewall = .5 * numpy.cos( theta * numpy.pi / 180 )

  # construct mesh
  xnodes = ynodes = numpy.linspace(0,1,nelems+1)
  domain, ns.x = mesh.rectilinear( [ xnodes, ynodes ] )

  # prepare bases
  ns.cbasis, ns.mubasis = function.chain([
    domain.basis('spline', degree=2),
    domain.basis('spline', degree=2)
  ])

  # polulate namespace
  ns.c = 'cbasis_n ?lhs_n'
  ns.c0 = 'cbasis_n ?lhs0_n'
  ns.mu = 'mubasis_n ?lhs_n'
  ns.f = '(6 c0 - 2 c0^3 - 4 c) / epsilon^2'

  # construct initial condition
  if init == 'random':
    numpy.random.seed(0)
    lhs0 = numpy.random.normal(0, .5, ns.cbasis.shape)
  elif init == 'bubbles':
    R1 = .25
    R2 = numpy.sqrt(.5) * R1 # area2 = .5 * area1
    ns.cbubble1 = function.tanh((R1-function.norm2(ns.x-(.5+R2/numpy.sqrt(2)+.8*ns.epsilon)))/ns.epsilon)
    ns.cbubble2 = function.tanh((R2-function.norm2(ns.x-(.5-R1/numpy.sqrt(2)-.8*ns.epsilon)))/ns.epsilon)
    sqr = domain.integral('(c - cbubble1 - cbubble2 - 1)^2 + mu^2' @ ns, geometry=ns.x, degree=4)
    lhs0 = solver.optimize('lhs', sqr)
  else:
    raise Exception( 'unknown init %r' % init )

  # construct residual
  res = domain.integral('epsilon^2 cbasis_n,k mu_,k + mubasis_n (mu + f) - mubasis_n,k c_,k' @ ns, geometry=ns.x, degree=4)
  res -= domain.boundary.integral('mubasis_n ewall' @ ns, geometry=ns.x, degree=4)
  inertia = domain.integral('cbasis_n c' @ ns, geometry=ns.x, degree=4)

  # solve time dependent problem
  nsteps = numeric.round(maxtime/timestep)
  makeplots = MakePlots(domain, nsteps, ns) if withplots else lambda *args: None
  for istep, lhs in log.enumerate('timestep', solver.impliciteuler('lhs', target0='lhs0', residual=res, inertia=inertia, timestep=timestep, lhs0=lhs0)):
    makeplots(lhs)
    if istep == nsteps:
      break

  return lhs0, lhs