Esempio n. 1
0
def test_bases(mu, case):
    domain, vbasis, pbasis = case.domain, case.basis('v'), case.basis('p')
    trfgeom = case.geometry(mu)
    refgeom = case.refgeom

    a_vbasis, a_pbasis = piola_bases(mu, case)

    J = trfgeom.grad(case.geometry())
    detJ = fn.determinant(J)
    b_vbasis = fn.matmat(vbasis, J.transpose()) / detJ
    b_pbasis = pbasis / detJ

    Z = case.geometry(mu).grad(case.geometry())
    detZ = fn.determinant(Z)
    zdiff = np.sqrt(domain.integrate((Z - J)**2 * fn.J(refgeom), ischeme='gauss9').export('dense'))
    np.testing.assert_almost_equal(zdiff, 0.0)

    c_vbasis = fn.matmat(vbasis, Z.transpose()) / detZ
    c_pbasis = pbasis

    pdiff = np.sqrt(domain.integrate((a_pbasis - b_pbasis)**2 * fn.J(refgeom), ischeme='gauss9'))
    np.testing.assert_almost_equal(pdiff, 0.0)
    pdiff = domain.integrate((a_pbasis - c_pbasis)**2 * fn.J(refgeom), ischeme='gauss9')
    np.testing.assert_almost_equal(pdiff, 0.0)

    vdiff = np.sqrt(domain.integrate((a_vbasis - b_vbasis)**2 * fn.J(refgeom), ischeme='gauss9').export('dense'))
    np.testing.assert_almost_equal(vdiff, 0.0)
    vdiff = domain.integrate((a_vbasis - c_vbasis)**2 * fn.J(refgeom), ischeme='gauss9').export('dense')
    np.testing.assert_almost_equal(vdiff, 0.0)
Esempio n. 2
0
 def test_nonlinear_diagonalshift(self):
     nelems = 10
     domain, geom = mesh.rectilinear([nelems, 1])
     geom *= [2 * numpy.pi / nelems, 1]
     ubasis = domain.basis('spline', degree=2).vector(2)
     u = ubasis.dot(function.Argument('dofs', [len(ubasis)]))
     Geom = [.5 * geom[0], geom[1] + function.cos(geom[0])
             ] + u  # compress by 50% and buckle
     cons = solver.minimize('dofs',
                            domain.boundary['left,right'].integral(
                                (u**2).sum(0), degree=4),
                            droptol=1e-15).solve()
     strain = .5 * (function.outer(Geom.grad(geom), axis=1).sum(0) -
                    function.eye(2))
     energy = domain.integral(
         ((strain**2).sum([0, 1]) + 150 *
          (function.determinant(Geom.grad(geom)) - 1)**2) *
         function.J(geom),
         degree=6)
     nshift = 0
     for iiter, (lhs, info) in enumerate(
             solver.minimize('dofs', energy, constrain=cons)):
         self.assertLess(iiter, 38)
         if info.shift:
             nshift += 1
         if info.resnorm < self.tol:
             break
     self.assertEqual(nshift, 9)
Esempio n. 3
0
 def setUp(self):
     super().setUp()
     domain, geom = mesh.rectilinear([numpy.linspace(0, 1, 9)] * 2)
     ubasis = domain.basis('std', degree=2)
     if self.vector:
         u = ubasis.vector(2).dot(
             function.Argument('dofs', [len(ubasis) * 2]))
     else:
         u = (ubasis[:, numpy.newaxis] *
              function.Argument('dofs', [len(ubasis), 2])).sum(0)
     Geom = geom * [1.1, 1] + u
     self.cons = solver.optimize('dofs',
                                 domain.boundary['left,right'].integral(
                                     (u**2).sum(0), degree=4),
                                 droptol=1e-15)
     self.boolcons = ~numpy.isnan(self.cons)
     strain = .5 * (function.outer(Geom.grad(geom), axis=1).sum(0) -
                    function.eye(2))
     self.energy = domain.integral(
         ((strain**2).sum([0, 1]) + 20 *
          (function.determinant(Geom.grad(geom)) - 1)**2) *
         function.J(geom),
         degree=6)
     self.residual = self.energy.derivative('dofs')
     self.tol = 1e-10
Esempio n. 4
0
 def setUp(self):
   super().setUp()
   domain, geom = mesh.rectilinear([numpy.linspace(0,1,9)] * 2)
   ubasis = domain.basis('std', degree=2).vector(2)
   u = ubasis.dot(function.Argument('dofs', [len(ubasis)]))
   Geom = geom * [1.1, 1] + u
   self.cons = solver.minimize('dofs', domain.boundary['left,right'].integral((u**2).sum(0), degree=4), droptol=1e-15).solve()
   self.boolcons = ~numpy.isnan(self.cons)
   strain = .5 * (function.outer(Geom.grad(geom), axis=1).sum(0) - function.eye(2))
   self.energy = domain.integral(((strain**2).sum([0,1]) + 20*(function.determinant(Geom.grad(geom))-1)**2)*function.J(geom), degree=6)
   self.residual = self.energy.derivative('dofs')
   self.tol = 1e-10
Esempio n. 5
0
class PiolaScalarTransform(MuCallable):

    _ident_ = 'PiolaScalarTransform'

    def __init__(self, *deps):
        super().__init__((), deps)

    def evaluate(self, case, mu, cont):
        assert all(c is None for c in cont)
        refgeom = case.geometry()
        trfgeom = case.geometry(mu)
        J = trfgeom.grad(refgeom)
        return 1 / fn.determinant(J)
Esempio n. 6
0
def piola_bases(mu, case):
    trfgeom = case.geometry(mu)
    refgeom = case.refgeom

    J = trfgeom.grad(refgeom)
    detJ = fn.determinant(J)
    vnbasis, vtbasis, pbasis = fn.chain([
        case.domain.basis('spline', degree=(3,2))[:,_] * J[:,0] / detJ,
        case.domain.basis('spline', degree=(2,3))[:,_] * J[:,1] / detJ,
        case.domain.basis('spline', degree=2) / detJ,
    ])
    vbasis = vnbasis + vtbasis

    return vbasis, pbasis
Esempio n. 7
0
def method_library(g, method='Elliptic', degree=None):
    assert len(g) == 2

    if degree is None:
        degree = g.ischeme * 4

    target = function.Argument('target', [len(g.x)])

    if method in ['Elliptic', 'Elliptic_unscaled']:
        G = metric_tensor(g, target)
        J = jacobian(g, target)
        ((g11, g12), (g21, g22)) = G
        s = function.stack
        G = s([s([g22, -g12]), s([-g12, g11])], axis=1)
        lapl = (J.grad(g.geom) * G[None]).sum([1, 2])
        res = (g.basis.vector(2) * lapl).sum(-1)
        res /= (1 if method == 'Elliptic_unscaled' else (g11 + g22 + 0.0001))
        res = g.domain.integral(res, geometry=g.geom, degree=degree)

    elif method == 'Elliptic_forward':
        res = function.trace(metric_tensor(g, target))
        res = g.domain.integral(res, geometry=g.geom,
                                degree=degree).derivative('target')

    elif method == 'Winslow':
        res = function.trace(metric_tensor(g, target)) / function.determinant(
            jacobian(g, target))
        res = g.domain.integral(res, geometry=g.geom,
                                degree=degree).derivative('target')

    elif method == 'Elliptic_partial':
        x = g.basis.vector(2).dot(target)
        res = (g.basis.vector(2).grad(x) * (g.geom.grad(x)[None])).sum([1, 2])
        res = g.domain.integral(res, geometry=x, degree=degree)

    elif method == 'Liao':
        G = metric_tensor(g, target)
        ((g11, g12), (g21, g22)) = G
        res = g.domain.integral(g11**2 + 2 * g12**2 + g22**2,
                                geometry=g.geom,
                                degree=degree).derivative('target')

    else:
        raise 'Unknown method {}'.format(method)

    return res
Esempio n. 8
0
 def test_nonlinear_diagonalshift(self):
   nelems = 10
   domain, geom = mesh.rectilinear([nelems,1])
   geom *= [2*numpy.pi/nelems, 1]
   ubasis = domain.basis('spline', degree=2).vector(2)
   u = ubasis.dot(function.Argument('dofs', [len(ubasis)]))
   Geom = [.5 * geom[0], geom[1] + function.cos(geom[0])] + u # compress by 50% and buckle
   cons = solver.minimize('dofs', domain.boundary['left,right'].integral((u**2).sum(0), degree=4), droptol=1e-15).solve()
   strain = .5 * (function.outer(Geom.grad(geom), axis=1).sum(0) - function.eye(2))
   energy = domain.integral(((strain**2).sum([0,1]) + 150*(function.determinant(Geom.grad(geom))-1)**2)*function.J(geom), degree=6)
   nshift = 0
   for iiter, (lhs, info) in enumerate(solver.minimize('dofs', energy, constrain=cons)):
     self.assertLess(iiter, 90)
     if info.shift:
       nshift += 1
     if info.resnorm < self.tol:
       break
   self.assertEqual(nshift, 60)
Esempio n. 9
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. 10
0
    angle = np.linspace(0, 2 * np.pi, cpts.shape[0], endpoint=False) - cylrot
    angle = np.hstack([[angle[-1]], angle[:-1]])
    upts = radius * np.vstack([np.cos(angle), np.sin(angle)]).T

    interp = np.linspace(0, 1, nelems + 3)**3
    cc = np.vstack([(1 - i) * cpts + i * upts for i in interp])
    geom = fn.asarray([basis.dot(cc[:, 0]), basis.dot(cc[:, 1])])

    return domain, refgeom, geom


def mk_bases(case, piola):
    if piola:
        mu = case.parameter()
        J = case.geometry(mu).grad(case.refgeom)
        detJ = fn.determinant(J)
        bases = [
            case.domain.basis('spline', degree=(3, 2))[:, _] * J[:, 0] / detJ,
            case.domain.basis('spline', degree=(2, 3))[:, _] * J[:, 1] / detJ,
            case.domain.basis('spline', degree=2) / detJ,
        ]
    else:
        nr, na = case.domain.shape
        rkts = np.arange(nr + 1)
        pkts = np.arange(na + 1)
        rmul = [2] * len(rkts)
        rmul[0] = 3
        rmul[-1] = 3
        pmul = [2] * len(pkts)

        thbasis = case.domain.basis(
Esempio n. 11
0
def elliptic_control_mapping(g,
                             f,
                             eps=1e-4,
                             ltol=1e-5,
                             degree=None,
                             **solveargs):
    '''
        Nutils implementation of the corresponding method from the ``fsol`` module.
        It's slower but accepts any control mapping function. Unline in the ``fsol``
        case, all derivatives are computed automatically.
    '''

    assert len(g) == len(f) == 2
    assert eps >= 0

    if degree is None:
        degree = 4 * g.ischeme

    if isinstance(f, go.TensorGridObject):
        if not (f.knotvector <= g.knotvector).all():
            raise AssertionError('''
                    Error, the control-mapping knotvector needs
                    to be a subset of the target GridObject knotvector
                ''')

        # refine, just in case
        f = go.refine_GridObject(f, g.knotvector)
        G = metric_tensor(g, f.x)
    else:
        # f is a nutils function
        jacT = function.transpose(f.grad(g.geom))
        G = function.outer(jacT, jacT).sum(-1)

    target = function.Argument('target', [len(g.x)])
    basis = g.basis.vector(2)
    x = basis.dot(target)

    (g11, g12), (g21, g22) = metric_tensor(g, target)
    lapl = g22 * x.grad(g.geom)[:, 0].grad(g.geom)[:, 0] - \
        2 * g12 * x.grad(g.geom)[:, 0].grad(g.geom)[:, 1] + \
        g11 * x.grad(g.geom)[:, 1].grad(g.geom)[:, 1]

    G_tilde = G / function.sqrt(function.determinant(G))
    (G11, G12), (G12, G22) = G_tilde

    P = g11 * G12.grad(g.geom)[1] - g12 * G11.grad(g.geom)[1]
    Q = 0.5 * (g11 * G22.grad(g.geom)[0] - g22 * G11.grad(g.geom)[0])
    S = g12 * G22.grad(g.geom)[0] - g22 * G12.grad(g.geom)[0]
    R = 0.5 * (g11 * G22.grad(g.geom)[1] - g22 * G11.grad(g.geom)[1])

    control_term = -x.grad( g.geom )[:, 0] * ( G22*(P-Q) + G12*(S-R) ) \
                   -x.grad( g.geom )[:, 1] * ( G11*(R-S) + G12*(Q-P) )

    scale = g11 + g22 + eps

    res = g.domain.integral((basis * (control_term + lapl)).sum(-1) / scale,
                            geometry=g.geom,
                            degree=degree)

    init, cons = g.x, g.cons
    lhs = solver.newton('target', res, lhs0=init,
                        constrain=cons.where).solve(ltol, **solveargs)

    g.x = lhs
Esempio n. 12
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. 13
0
 def jacdet(self):
     grd = self.mapping.grad(self.geom)
     return function.determinant(grd) if len(self) > 1 else function.sqrt(
         grd.sum(-2))
Esempio n. 14
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. 15
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. 16
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. 17
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