Exemple #1
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
Exemple #2
0
def main(nelems:int, etype:str, btype:str, degree:int, poisson:float, angle:float, restol:float, trim:bool):
  '''
  Deformed hyperelastic plate.

  .. arguments::

     nelems [10]
       Number of elements along edge.
     etype [square]
       Type of elements (square/triangle/mixed).
     btype [std]
       Type of basis function (std/spline).
     degree [1]
       Polynomial degree.
     poisson [.25]
       Poisson's ratio, nonnegative and stricly smaller than 1/2.
     angle [20]
       Rotation angle for right clamp (degrees).
     restol [1e-10]
       Newton tolerance.
     trim [no]
       Create circular-shaped hole.
  '''

  domain, geom = mesh.unitsquare(nelems, etype)
  if trim:
    domain = domain.trim(function.norm2(geom-.5)-.2, maxrefine=2)
  bezier = domain.sample('bezier', 5)

  ns = function.Namespace(fallback_length=domain.ndims)
  ns.x = geom
  ns.angle = angle * numpy.pi / 180
  ns.lmbda = 2 * poisson
  ns.mu = 1 - 2 * poisson
  ns.ubasis = domain.basis(btype, degree=degree)
  ns.u_i = 'ubasis_k ?lhs_ki'
  ns.X_i = 'x_i + u_i'
  ns.strain_ij = '.5 (d(u_i, x_j) + d(u_j, x_i))'
  ns.energy = 'lmbda strain_ii strain_jj + 2 mu strain_ij strain_ij'

  sqr = domain.boundary['left'].integral('u_k u_k J(x)' @ ns, degree=degree*2)
  sqr += domain.boundary['right'].integral('((u_0 - x_1 sin(2 angle) - cos(angle) + 1)^2 + (u_1 - x_1 (cos(2 angle) - 1) + sin(angle))^2) J(x)' @ ns, degree=degree*2)
  cons = solver.optimize('lhs', sqr, droptol=1e-15)

  energy = domain.integral('energy J(x)' @ ns, degree=degree*2)
  lhs0 = solver.optimize('lhs', energy, constrain=cons)
  X, energy = bezier.eval(['X', 'energy'] @ ns, lhs=lhs0)
  export.triplot('linear.png', X, energy, tri=bezier.tri, hull=bezier.hull)

  ns.strain_ij = '.5 (d(u_i, x_j) + d(u_j, x_i) + d(u_k, x_i) d(u_k, x_j))'
  ns.energy = 'lmbda strain_ii strain_jj + 2 mu strain_ij strain_ij'

  energy = domain.integral('energy J(x)' @ ns, degree=degree*2)
  lhs1 = solver.minimize('lhs', energy, arguments=dict(lhs=lhs0), constrain=cons).solve(restol)
  X, energy = bezier.eval(['X', 'energy'] @ ns, lhs=lhs1)
  export.triplot('nonlinear.png', X, energy, tri=bezier.tri, hull=bezier.hull)

  return lhs0, lhs1
 def test_unknowntarget(self):
   err = self.domain.integral('(sqrt(1 - u) - .5)^2' @ self.ns, degree=2)
   with self.assertRaises(ValueError):
     dofs = solver.optimize(['dofs', 'other'], err, tol=1e-10)
   dofs = solver.optimize(['dofs', 'other'], err, tol=1e-10, droptol=1e-10)
   self.assertEqual(list(dofs), ['dofs'])
   dofs = solver.optimize(['other'], err, tol=1e-10, droptol=1e-10)
   self.assertEqual(dofs, {})
   with self.assertRaises(KeyError):
     dofs = solver.optimize('other', err, tol=1e-10, droptol=1e-10)
Exemple #4
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
Exemple #5
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 = domain.basis('std', degree=degree).vector(domain.ndims)
  ns.pbasis = domain.basis('std', degree=degree-1)
  ns.u_i = 'ubasis_ni ?u_n'
  ns.p = 'pbasis_n ?p_n'
  ns.stress_ij = '(d(u_i, x_j) + d(u_j, x_i)) / Re - p δ_ij'

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

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

  ucons = numpy.choose(numpy.isnan(lidcons), [lidcons, wallcons])
  pcons = numpy.zeros(len(ns.pbasis), dtype=bool)
  pcons[-1] = True # constrain pressure to zero in a point
  cons = dict(u=ucons, p=pcons)

  ures = domain.integral('d(ubasis_ni, x_j) stress_ij d:x' @ ns, degree=degree*2)
  pres = domain.integral('pbasis_n d(u_k, x_k) d:x' @ ns, degree=degree*2)
  with treelog.context('stokes'):
    state0 = solver.solve_linear(('u', 'p'), (ures, pres), constrain=cons)
    postprocess(domain, ns, **state0)

  ures += domain.integral('.5 (ubasis_ni d(u_i, x_j) - d(ubasis_ni, x_j) u_i) u_j d:x' @ ns, degree=degree*3)
  with treelog.context('navierstokes'):
    state1 = solver.newton(('u', 'p'), (ures, pres), arguments=state0, constrain=cons).solve(tol=1e-10)
    postprocess(domain, ns, **state1)

  return state0, state1
Exemple #6
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)
 def setUp(self):
     super().setUp()
     domain, geom = mesh.rectilinear([numpy.linspace(0, 1, 9)] * 2)
     ubasis = domain.basis('std', degree=2)
     if self.vector:
         u = ubasis.vector(2).dot(
             function.Argument('dofs', [len(ubasis) * 2]))
     else:
         u = (ubasis[:, numpy.newaxis] *
              function.Argument('dofs', [len(ubasis), 2])).sum(0)
     Geom = geom * [1.1, 1] + u
     self.cons = solver.optimize('dofs',
                                 domain.boundary['left,right'].integral(
                                     (u**2).sum(0), degree=4),
                                 droptol=1e-15)
     self.boolcons = ~numpy.isnan(self.cons)
     strain = .5 * (function.outer(Geom.grad(geom), axis=1).sum(0) -
                    function.eye(2))
     self.energy = domain.integral(
         ((strain**2).sum([0, 1]) + 20 *
          (function.determinant(Geom.grad(geom)) - 1)**2) *
         function.J(geom),
         degree=6)
     self.residual = self.energy.derivative('dofs')
     self.tol = 1e-10
def postprocess(domain, ns, every=.05, spacing=.01, **arguments):

  div = domain.integral('d(u_k, x_k)^2 J(x)' @ ns, degree=1).eval(**arguments)**.5
  treelog.info('velocity divergence: {:.2e}'.format(div)) # confirm that velocity is pointwise divergence-free

  ns = ns.copy_() # copy namespace so that we don't modify the calling argument
  ns.streambasis = domain.basis('std', degree=2)[1:] # remove first dof to obtain non-singular system
  ns.stream = 'streambasis_n ?streamdofs_n' # stream function
  ns.ε = function.levicivita(2)
  sqr = domain.integral('sum:i((u_i - ε_ij d(stream, x_j))^2) J(x)' @ ns, degree=4)
  arguments['streamdofs'] = solver.optimize('streamdofs', sqr, arguments=arguments) # compute streamlines

  bezier = domain.sample('bezier', 9)
  x, u, p, stream = bezier.eval(['x', 'norm2(u)', 'p', 'stream'] @ ns, **arguments)
  with export.mplfigure('flow.png') as fig: # plot velocity as field, pressure as contours, streamlines as dashed
    ax = fig.add_axes([.1,.1,.8,.8], yticks=[], aspect='equal')
    import matplotlib.collections
    ax.add_collection(matplotlib.collections.LineCollection(x[bezier.hull], colors='w', linewidths=.5, alpha=.2))
    ax.tricontour(x[:,0], x[:,1], bezier.tri, stream, 16, colors='k', linestyles='dotted', linewidths=.5, zorder=9)
    caxu = fig.add_axes([.1,.1,.03,.8], title='velocity')
    imu = ax.tripcolor(x[:,0], x[:,1], bezier.tri, u, shading='gouraud', cmap='jet')
    fig.colorbar(imu, cax=caxu)
    caxu.yaxis.set_ticks_position('left')
    caxp = fig.add_axes([.87,.1,.03,.8], title='pressure')
    imp = ax.tricontour(x[:,0], x[:,1], bezier.tri, p, 16, cmap='gray', linestyles='solid')
    fig.colorbar(imp, cax=caxp)
Exemple #9
0
 def test_nonlinear(self):
     err = self.domain.boundary['bottom'].integral(
         '(u + .25 u^3 - 1.25)^2 d:geom' @ self.ns, degree=6)
     cons = solver.optimize('dofs', err, droptol=1e-15, tol=1e-15)
     numpy.testing.assert_almost_equal(
         cons,
         numpy.take([1, numpy.nan], [0, 1, 1, 0, 1, 1, 0, 1, 1]),
         decimal=15)
Exemple #10
0
 def test_linear(self):
   ns = self.ns
   ns.u = 'ubasis_n ?dofs_n'
   err = self.domain.boundary['bottom'].integral(ns.eval_('(u - 1)^2'), geometry=ns.geom, degree=2)
   cons = solver.optimize('dofs', err, droptol=1e-15)
   isnan = numpy.isnan(cons)
   self.assertTrue(numpy.equal(isnan, [0,1,1,0,1,1,0,1,1]).all())
   numpy.testing.assert_almost_equal(cons[~isnan], 1, decimal=15)
Exemple #11
0
 def test_nonlinear_multipleroots(self):
   ns = self.ns
   ns.u = 'ubasis_n ?dofs_n'
   ns.gu = 'u + u^2'
   err = self.domain.boundary['bottom'].integral(ns.eval_('(gu - .75)^2'), geometry=ns.geom, degree=2)
   cons = solver.optimize('dofs', err, droptol=1e-15, lhs0=numpy.ones(len(ns.ubasis)), newtontol=1e-10)
   isnan = numpy.isnan(cons)
   self.assertTrue(numpy.equal(isnan, [0,1,1,0,1,1,0,1,1]).all())
   numpy.testing.assert_almost_equal(cons[~isnan], .5, decimal=15)
Exemple #12
0
 def test_linear(self):
     ns = self.ns
     ns.u = 'ubasis_n ?dofs_n'
     err = self.domain.boundary['bottom'].integral(ns.eval_('(u - 1)^2'),
                                                   geometry=ns.geom,
                                                   degree=2)
     cons = solver.optimize('dofs', err, droptol=1e-15)
     isnan = numpy.isnan(cons)
     self.assertTrue(numpy.equal(isnan, [0, 1, 1, 0, 1, 1, 0, 1, 1]).all())
     numpy.testing.assert_almost_equal(cons[~isnan], 1, decimal=15)
Exemple #13
0
 def test_nonlinear_multipleroots(self):
     err = self.domain.boundary['bottom'].integral(
         '(u + u^2 - .75)^2' @ self.ns, degree=2)
     cons = solver.optimize('dofs',
                            err,
                            droptol=1e-15,
                            lhs0=numpy.ones(len(self.ns.ubasis)),
                            tol=1e-10)
     numpy.testing.assert_almost_equal(
         cons,
         numpy.take([.5, numpy.nan], [0, 1, 1, 0, 1, 1, 0, 1, 1]),
         decimal=15)
Exemple #14
0
def postprocess(domain, ns, every=.05, spacing=.01, **arguments):

    ns = ns.copy_(
    )  # copy namespace so that we don't modify the calling argument
    ns.streambasis = domain.basis(
        'std', degree=2)[1:]  # remove first dof to obtain non-singular system
    ns.stream = 'streambasis_n ?streamdofs_n'  # stream function
    sqr = domain.integral(
        '((u_0 - stream_,1)^2 + (u_1 + stream_,0)^2) d:x' @ ns, degree=4)
    arguments['streamdofs'] = solver.optimize(
        'streamdofs', sqr, arguments=arguments)  # compute streamlines

    bezier = domain.sample('bezier', 9)
    x, u, p, stream = bezier.eval(['x_i', 'sqrt(u_k u_k)', 'p', 'stream'] @ ns,
                                  **arguments)
    with export.mplfigure(
            'flow.png'
    ) as fig:  # plot velocity as field, pressure as contours, streamlines as dashed
        ax = fig.add_axes([.1, .1, .8, .8], yticks=[], aspect='equal')
        import matplotlib.collections
        ax.add_collection(
            matplotlib.collections.LineCollection(x[bezier.hull],
                                                  colors='w',
                                                  linewidths=.5,
                                                  alpha=.2))
        ax.tricontour(x[:, 0],
                      x[:, 1],
                      bezier.tri,
                      stream,
                      16,
                      colors='k',
                      linestyles='dotted',
                      linewidths=.5,
                      zorder=9)
        caxu = fig.add_axes([.1, .1, .03, .8], title='velocity')
        imu = ax.tripcolor(x[:, 0],
                           x[:, 1],
                           bezier.tri,
                           u,
                           shading='gouraud',
                           cmap='jet')
        fig.colorbar(imu, cax=caxu)
        caxu.yaxis.set_ticks_position('left')
        caxp = fig.add_axes([.87, .1, .03, .8], title='pressure')
        imp = ax.tricontour(x[:, 0],
                            x[:, 1],
                            bezier.tri,
                            p,
                            16,
                            cmap='gray',
                            linestyles='solid')
        fig.colorbar(imp, cax=caxp)
Exemple #15
0
def main(
    nelems: 'number of elements' = 12,
    lmbda: 'first lamé constant' = 1.,
    mu: 'second lamé constant' = 1.,
    degree: 'polynomial degree' = 2,
    withplots: 'create plots' = True,
    solvetol: 'solver tolerance' = 1e-10,
):

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

    # create namespace
    ns = function.Namespace(default_geometry_name='x0')
    ns.x0 = geom
    ns.basis = domain.basis('spline', degree=degree).vector(2)
    ns.u_i = 'basis_ni ?lhs_n'
    ns.x_i = 'x0_i + u_i'
    ns.lmbda = lmbda
    ns.mu = mu
    ns.strain_ij = '(u_i,j + u_j,i) / 2'
    ns.stress_ij = 'lmbda strain_kk δ_ij + 2 mu strain_ij'

    # construct dirichlet boundary constraints
    sqr = domain.boundary['left'].integral('u_k u_k' @ ns,
                                           geometry=ns.x0,
                                           degree=2)
    sqr += domain.boundary['right'].integral('(u_0 - .5)^2' @ ns,
                                             geometry=ns.x0,
                                             degree=2)
    cons = solver.optimize('lhs', sqr, droptol=1e-15)

    # construct residual
    res = domain.integral('basis_ni,j stress_ij' @ ns,
                          geometry=ns.x0,
                          degree=2)

    # solve system and substitute the solution in the namespace
    lhs = solver.solve_linear('lhs', res, constrain=cons)
    ns |= dict(lhs=lhs)

    # plot solution
    if withplots:
        points, colors = domain.elem_eval([ns.x, ns.stress[0, 1]],
                                          ischeme='bezier3',
                                          separate=True)
        with plot.PyPlot('stress', ndigits=0) as plt:
            plt.mesh(points, colors, tight=False)
            plt.colorbar()

    return lhs, cons
Exemple #16
0
 def test_nonlinear_multipleroots(self):
     ns = self.ns
     ns.u = 'ubasis_n ?dofs_n'
     ns.gu = 'u + u^2'
     err = self.domain.boundary['bottom'].integral(ns.eval_('(gu - .75)^2'),
                                                   geometry=ns.geom,
                                                   degree=2)
     cons = solver.optimize('dofs',
                            err,
                            droptol=1e-15,
                            lhs0=numpy.ones(len(ns.ubasis)),
                            newtontol=1e-10)
     isnan = numpy.isnan(cons)
     self.assertTrue(numpy.equal(isnan, [0, 1, 1, 0, 1, 1, 0, 1, 1]).all())
     numpy.testing.assert_almost_equal(cons[~isnan], .5, decimal=15)
Exemple #17
0
def main(nelems: int, etype: str, btype: str, degree: int, poisson: float):
    '''
  Horizontally loaded linear elastic plate.

  .. arguments::

     nelems [10]
       Number of elements along edge.
     etype [square]
       Type of elements (square/triangle/mixed).
     btype [std]
       Type of basis function (std/spline), with availability depending on the
       configured element type.
     degree [1]
       Polynomial degree.
     poisson [.25]
       Poisson's ratio, nonnegative and strictly smaller than 1/2.
  '''

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

    ns = function.Namespace()
    ns.x = geom
    ns.basis = domain.basis(btype, degree=degree).vector(2)
    ns.u_i = 'basis_ni ?lhs_n'
    ns.X_i = 'x_i + u_i'
    ns.lmbda = 2 * poisson
    ns.mu = 1 - 2 * poisson
    ns.strain_ij = '(d(u_i, x_j) + d(u_j, x_i)) / 2'
    ns.stress_ij = 'lmbda strain_kk δ_ij + 2 mu strain_ij'

    sqr = domain.boundary['left'].integral('u_k u_k J(x)' @ ns,
                                           degree=degree * 2)
    sqr += domain.boundary['right'].integral('(u_0 - .5)^2 J(x)' @ ns,
                                             degree=degree * 2)
    cons = solver.optimize('lhs', sqr, droptol=1e-15)

    res = domain.integral('d(basis_ni, x_j) stress_ij J(x)' @ ns,
                          degree=degree * 2)
    lhs = solver.solve_linear('lhs', res, constrain=cons)

    bezier = domain.sample('bezier', 5)
    X, sxy = bezier.eval(['X', 'stress_01'] @ ns, lhs=lhs)
    export.triplot('shear.png', X, sxy, tri=bezier.tri, hull=bezier.hull)

    return cons, lhs
Exemple #18
0
def main(
    nelems: 'number of elements' = 10,
    degree: 'polynomial degree' = 1,
    basistype: 'basis function' = 'spline',
    solvetol: 'solver tolerance' = 1e-10,
    figures: 'create figures' = True,
  ):

  # construct mesh
  verts = numpy.linspace(0, numpy.pi/2, nelems+1)
  domain, geom = mesh.rectilinear([verts, verts])

  # create namespace
  ns = function.Namespace()
  ns.x = geom
  ns.basis = domain.basis(basistype, degree=degree)
  ns.u = 'basis_n ?lhs_n'
  ns.fx = 'sin(x_0)'
  ns.fy = 'exp(x_1)'

  # construct residual
  res = domain.integral('-basis_n,i u_,i' @ ns, geometry=ns.x, degree=degree*2)
  res += domain.boundary['top'].integral('basis_n fx fy' @ ns, geometry=ns.x, degree=degree*2)

  # construct dirichlet constraints
  sqr = domain.boundary['left'].integral('u^2' @ ns, geometry=ns.x, degree=degree*2)
  sqr += domain.boundary['bottom'].integral('(u - fx)^2' @ ns, geometry=ns.x, degree=degree*2)
  cons = solver.optimize('lhs', sqr, droptol=1e-15)

  # find lhs such that res == 0 and substitute this lhs in the namespace
  lhs = solver.solve_linear('lhs', res, constrain=cons)
  ns = ns(lhs=lhs)

  # plot solution
  if figures:
    points, colors = domain.elem_eval([ns.x, ns.u], ischeme='bezier9', separate=True)
    with plot.PyPlot('solution', index=nelems) as plt:
      plt.mesh(points, colors, cmap='jet')
      plt.colorbar()

  # evaluate error against exact solution fx fy
  err = domain.integrate('(u - fx fy)^2' @ ns, geometry=ns.x, degree=degree*2)**.5
  log.user('L2 error: {:.2e}'.format(err))

  return cons, lhs, err
Exemple #19
0
def main(
    nelems: 'number of elements' = 12,
    lmbda: 'first lamé constant' = 1.,
    mu: 'second lamé constant' = 1.,
    degree: 'polynomial degree' = 2,
    withplots: 'create plots' = True,
    solvetol: 'solver tolerance' = 1e-10,
 ):

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

  # create namespace
  ns = function.Namespace(default_geometry_name='x0')
  ns.x0 = geom
  ns.basis = domain.basis('spline', degree=degree).vector(2)
  ns.u_i = 'basis_ni ?lhs_n'
  ns.x_i = 'x0_i + u_i'
  ns.lmbda = lmbda
  ns.mu = mu
  ns.strain_ij = '(u_i,j + u_j,i) / 2'
  ns.stress_ij = 'lmbda strain_kk δ_ij + 2 mu strain_ij'

  # construct dirichlet boundary constraints
  sqr = domain.boundary['left'].integral('u_k u_k' @ ns, geometry=ns.x0, degree=2)
  sqr += domain.boundary['right'].integral('(u_0 - .5)^2' @ ns, geometry=ns.x0, degree=2)
  cons = solver.optimize('lhs', sqr, droptol=1e-15)

  # construct residual
  res = domain.integral('basis_ni,j stress_ij' @ ns, geometry=ns.x0, degree=2)

  # solve system and substitute the solution in the namespace
  lhs = solver.solve_linear('lhs', res, constrain=cons)
  ns |= dict(lhs=lhs)

  # plot solution
  if withplots:
    points, colors = domain.elem_eval([ns.x, ns.stress[0,1]], ischeme='bezier3', separate=True)
    with plot.PyPlot('stress', ndigits=0) as plt:
      plt.mesh(points, colors, tight=False)
      plt.colorbar()

  return lhs, cons
Exemple #20
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
Exemple #21
0
def main(nelems: int, etype: str, btype: str, degree: int,
         epsilon: typing.Optional[float], contactangle: float, timestep: float,
         mtol: float, seed: int, circle: bool, stab: stab):
    '''
  Cahn-Hilliard equation on a unit square/circle.

  .. arguments::

     nelems [20]
       Number of elements along domain edge.
     etype [square]
       Type of elements (square/triangle/mixed).
     btype [std]
       Type of basis function (std/spline), with availability depending on the
       configured element type.
     degree [2]
       Polynomial degree.
     epsilon []
       Interface thickness; defaults to an automatic value based on the
       configured mesh density if left unspecified.
     contactangle [90]
       Wall contact angle in degrees.
     timestep [.01]
       Time step.
     mtol [.01]
       Threshold value for chemical potential peak to peak difference, used as
       a stop criterion.
     seed [0]
       Random seed for the initial condition.
     circle [no]
       Select circular domain as opposed to a unit square.
     stab [linear]
       Stabilization method (linear/optimal/none).
  '''

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

    domain, geom = mesh.unitsquare(nelems, etype)
    bezier = domain.sample('bezier', 5)  # sample for plotting

    ns = function.Namespace()
    if not circle:
        ns.x = geom
    else:
        angle = (geom - .5) * (numpy.pi / 2)
        ns.x = function.sin(angle) * function.cos(angle)[[1, 0
                                                          ]] / numpy.sqrt(2)
    ns.epsilon = epsilon
    ns.ewall = .5 * numpy.cos(contactangle * numpy.pi / 180)
    ns.cbasis = ns.mbasis = domain.basis('std', degree=degree)
    ns.c = 'cbasis_n ?c_n'
    ns.dc = 'cbasis_n (?c_n - ?c0_n)'
    ns.m = 'mbasis_n ?m_n'
    ns.F = '.5 (c^2 - 1)^2 / epsilon^2'
    ns.dF = stab.value
    ns.dt = timestep

    nrg_mix = domain.integral('F J(x)' @ ns, degree=7)
    nrg_iface = domain.integral('.5 sum:k(d(c, x_k)^2) J(x)' @ ns, degree=7)
    nrg_wall = domain.boundary.integral('(abs(ewall) + c ewall) J(x)' @ ns,
                                        degree=7)
    nrg = nrg_mix + nrg_iface + nrg_wall + domain.integral(
        '(dF - m dc - .5 dt epsilon^2 sum:k(d(m, x_k)^2)) J(x)' @ ns, degree=7)

    numpy.random.seed(seed)
    state = dict(c=numpy.random.normal(0, .5, ns.cbasis.shape),
                 m=numpy.random.normal(0, .5,
                                       ns.mbasis.shape))  # initial condition

    with treelog.iter.plain('timestep', itertools.count()) as steps:
        for istep in steps:

            E = sample.eval_integrals(nrg_mix, nrg_iface, nrg_wall, **state)
            treelog.user(
                'energy: {0:.3f} ({1[0]:.0f}% mixture, {1[1]:.0f}% interface, {1[2]:.0f}% wall)'
                .format(sum(E), 100 * numpy.array(E) / sum(E)))

            x, c, m = bezier.eval(['x', 'c', 'm'] @ ns, **state)
            export.triplot('phase.png', x, c, tri=bezier.tri, clim=(-1, 1))
            export.triplot('chempot.png', x, m, tri=bezier.tri)

            if numpy.ptp(m) < mtol:
                break

            state['c0'] = state['c']
            state = solver.optimize(['c', 'm'],
                                    nrg,
                                    arguments=state,
                                    tol=1e-10)

    return state
Exemple #22
0
def main(nelems: int, ndims: int, degree: int, timescale: float,
         newtontol: float, endtime: float):
    '''
  Burgers equation on a 1D or 2D periodic domain.

  .. arguments::

     nelems [20]
       Number of elements along a single dimension.
     ndims [1]
       Number of spatial dimensions.
     degree [1]
       Polynomial degree for discontinuous basis functions.
     timescale [.5]
       Fraction of timestep and element size: timestep=timescale/nelems.
     newtontol [1e-5]
       Newton tolerance.
     endtime [inf]
       Stopping time.
  '''

    domain, geom = mesh.rectilinear([numpy.linspace(0, 1, nelems + 1)] * ndims,
                                    periodic=range(ndims))

    ns = function.Namespace()
    ns.x = geom
    ns.basis = domain.basis('discont', degree=degree)
    ns.u = 'basis_n ?lhs_n'
    ns.f = '.5 u^2'
    ns.C = 1

    res = domain.integral('-basis_n,0 f d:x' @ ns, degree=5)
    res += domain.interfaces.integral(
        '-[basis_n] n_0 ({f} - .5 C [u] n_0) d:x' @ ns, degree=degree * 2)
    inertia = domain.integral('basis_n u d:x' @ ns, degree=5)

    sqr = domain.integral(
        '(u - exp(-?y_i ?y_i)(y_i = 5 (x_i - 0.5_i)))^2 d:x' @ ns, degree=5)
    lhs0 = solver.optimize('lhs', sqr)

    timestep = timescale / nelems
    bezier = domain.sample('bezier', 7)
    with treelog.iter.plain(
            'timestep',
            solver.impliciteuler('lhs',
                                 res,
                                 inertia,
                                 timestep=timestep,
                                 lhs0=lhs0,
                                 newtontol=newtontol)) as steps:
        for itime, lhs in enumerate(steps):
            x, u = bezier.eval(['x_i', 'u'] @ ns, lhs=lhs)
            export.triplot('solution.png',
                           x,
                           u,
                           tri=bezier.tri,
                           hull=bezier.hull,
                           clim=(0, 1))
            if itime * timestep >= endtime:
                break

    return lhs
Exemple #23
0
 def test_nanres(self):
     err = self.domain.integral('(sqrt(1 - u) - .5)^2' @ self.ns, degree=2)
     dofs = solver.optimize('dofs', err, tol=1e-10)
     numpy.testing.assert_almost_equal(dofs, .75)
def main(nrefine: int, traction: float, radius: float, poisson: float):
    '''
  Horizontally loaded linear elastic plate with IGA hole.

  .. arguments::

     nrefine [2]
       Number of uniform refinements starting from 1x2 base mesh.
     traction [.1]
       Far field traction (relative to Young's modulus).
     radius [.5]
       Cut-out radius.
     poisson [.3]
       Poisson's ratio, nonnegative and strictly smaller than 1/2.
  '''

    # create the coarsest level parameter domain
    domain, geom0 = mesh.rectilinear([1, 2])
    bsplinebasis = domain.basis('spline', degree=2)
    controlweights = numpy.ones(12)
    controlweights[1:3] = .5 + .25 * numpy.sqrt(2)
    weightfunc = bsplinebasis.dot(controlweights)
    nurbsbasis = bsplinebasis * controlweights / weightfunc

    # create geometry function
    indices = [0, 2], [1, 2], [2, 1], [2, 0]
    controlpoints = numpy.concatenate([
        numpy.take([0, 2**.5 - 1, 1], indices) * radius,
        numpy.take([0, .3, 1], indices) * (radius + 1) / 2,
        numpy.take([0, 1, 1], indices)
    ])
    geom = (nurbsbasis[:, numpy.newaxis] * controlpoints).sum(0)

    radiuserr = domain.boundary['left'].integral(
        (function.norm2(geom) - radius)**2 * function.J(geom0),
        degree=9).eval()**.5
    treelog.info('hole radius exact up to L2 error {:.2e}'.format(radiuserr))

    # refine domain
    if nrefine:
        domain = domain.refine(nrefine)
        bsplinebasis = domain.basis('spline', degree=2)
        controlweights = domain.project(weightfunc,
                                        onto=bsplinebasis,
                                        geometry=geom0,
                                        ischeme='gauss9')
        nurbsbasis = bsplinebasis * controlweights / weightfunc

    ns = function.Namespace()
    ns.x = geom
    ns.lmbda = 2 * poisson
    ns.mu = 1 - poisson
    ns.ubasis = nurbsbasis.vector(2)
    ns.u_i = 'ubasis_ni ?lhs_n'
    ns.X_i = 'x_i + u_i'
    ns.strain_ij = '(d(u_i, x_j) + d(u_j, x_i)) / 2'
    ns.stress_ij = 'lmbda strain_kk δ_ij + 2 mu strain_ij'
    ns.r2 = 'x_k x_k'
    ns.R2 = radius**2 / ns.r2
    ns.k = (3 - poisson) / (1 + poisson)  # plane stress parameter
    ns.scale = traction * (1 + poisson) / 2
    ns.uexact_i = 'scale (x_i ((k + 1) (0.5 + R2) + (1 - R2) R2 (x_0^2 - 3 x_1^2) / r2) - 2 δ_i1 x_1 (1 + (k - 1 + R2) R2))'
    ns.du_i = 'u_i - uexact_i'

    sqr = domain.boundary['top,bottom'].integral('(u_i n_i)^2 J(x)' @ ns,
                                                 degree=9)
    cons = solver.optimize('lhs', sqr, droptol=1e-15)
    sqr = domain.boundary['right'].integral('du_k du_k J(x)' @ ns, degree=20)
    cons = solver.optimize('lhs', sqr, droptol=1e-15, constrain=cons)

    # construct residual
    res = domain.integral('d(ubasis_ni, x_j) stress_ij J(x)' @ ns, degree=9)

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

    # vizualize result
    bezier = domain.sample('bezier', 9)
    X, stressxx = bezier.eval(['X', 'stress_00'] @ ns, lhs=lhs)
    export.triplot('stressxx.png',
                   X,
                   stressxx,
                   tri=bezier.tri,
                   hull=bezier.hull,
                   clim=(numpy.nanmin(stressxx), numpy.nanmax(stressxx)))

    # evaluate error
    err = domain.integral('<du_k du_k, sum:ij(d(du_i, x_j)^2)>_n J(x)' @ ns,
                          degree=9).eval(lhs=lhs)**.5
    treelog.user('errors: L2={:.2e}, H1={:.2e}'.format(*err))

    return err, cons, lhs
Exemple #25
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
Exemple #26
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
Exemple #27
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()
Exemple #28
0
def main(fname: str, degree: int, Δuload: unit['mm'], nsteps: int, E: unit['GPa'], nu: float, σyield: unit['MPa'], hardening: unit['GPa'], referencedata: typing.Optional[str], testing: bool):

  '''
  Plastic deformation of a perforated strip.

  .. arguments::

     fname [strip.msh]
       Mesh file with units in [mm]

     degree [1]
       Finite element interpolation order

     Δuload [5μm]
       Load boundary displacement steps

     nsteps [11]
       Number of load steps

     E [70GPa]
       Young's modulus

     nu [0.2]
       Poisson ratio

     σyield [243MPa]
       Yield strength

     hardening [2.25GPa]
       Hardening parameter

     referencedata [zienkiewicz.csv]
       Reference data file name

     testing [False]
       Use a 1 element mesh for testing

  .. presets::

     testing
       nsteps=30
       referencedata=
       testing=True
  '''

  # We commence with reading the mesh from the specified GMSH file, or, alternatively,
  # we continue with a single-element mesh for testing purposes.
  domain, geom = mesh.gmsh(pathlib.Path(__file__).parent/fname)
  Wdomain, Hdomain = domain.boundary['load'].integrate([function.J(geom),geom[1]*function.J(geom)], degree=1)
  Hdomain /= Wdomain

  if testing:
    domain, geom = mesh.rectilinear([numpy.linspace(0,Wdomain/2,2),numpy.linspace(0,Hdomain,2)])
    domain = domain.withboundary(hsymmetry='left', vsymmetry='bottom', load='top')

  # We next initiate the point set in which the constitutive bahvior will be evaluated.
  # Note that this is also the point set in which the history variable will be stored.
  gauss  = domain.sample('gauss', 2)

  # Elasto-plastic formulation
  # ==========================
  # The weak formulation is constructed using a `Namespace`, which is initialized and
  # populated with the necessary problem parameters and coordinate system. Note that the
  # coefficients `mu` and `λ` have been defined such that 'C_{ijkl}' is the fourth-order
  # **plane stress** elasticity tensor.

  ns = function.Namespace(fallback_length=domain.ndims)

  ns.x      = geom
  ns.Δuload = Δuload
  ns.mu     = E/(1-nu**2)*((1-nu)/2)
  ns.λ      = E/(1-nu**2)*nu
  ns.delta  = function.eye(domain.ndims)
  ns.C_ijkl = 'mu ( delta_ik delta_jl + delta_il delta_jk ) + λ delta_ij delta_kl'

  # We make use of a Lagrange finite element basis of arbitrary `degree`. Since we
  # approximate the displacement field, the basis functions are vector-valued. Both
  # the displacement field at the current load step `u` and displacement field of the
  # previous load step `u0` are approximated using this basis:

  ns.basis = domain.basis('std',degree=degree).vector(domain.ndims)

  ns.u0_i = 'basis_ni ?lhs0_n'
  ns.u_i  = 'basis_ni ?lhs_n'

  # This simulation is based on a standard elasto-plasticity model. In this
  # model the *total strain*, ε_kl = ½ (∂u_k/∂x_l + ∂u_l/∂x_k)', is comprised of an
  # elastic and a plastic part:
  #
  #   ε_kl = εe_kl + εp_kl
  #
  # The stress is related to the *elastic strain*, εe_kl, through Hooke's law:
  #
  #   σ_ij = C_ijkl εe_kl = C_ijkl (ε_kl - εp_kl)

  ns.ε_kl   = '(u_k,l + u_l,k) / 2'
  ns.ε0_kl  = '(u0_k,l + u0_l,k) / 2'
  ns.gbasis = gauss.basis()
  ns.εp0_ij = 'gbasis_n ?εp0_nij'
  ns.κ0     = 'gbasis_n ?κ0_n'

  ns.εp    = PlasticStrain(ns.ε, ns.ε0, ns.εp0, ns.κ0, E, nu, σyield, hardening)
  ns.εe_ij = 'ε_ij - εp_ij'
  ns.σ_ij  = 'C_ijkl εe_kl'

  # Note that the plasticity model is implemented through the user-defined function `PlasticStrain`,
  # which implements the actual yielding model including a standard return mapping algorithm. This
  # function is discussed in detail below.
  #
  # The components of the residual vector are then defined as:
  #
  #   r_n = ∫_Ω (∂N_ni/∂x_j) σ_ij dΩ

  res = domain.integral('basis_ni,j σ_ij d:x' @ ns, degree=2)

  # The problem formulation is completed by supplementing prescribed displacement boundary conditions
  # (for a load step), which are computed in the standard manner:

  sqr  = domain.boundary['hsymmetry,vsymmetry'].integral('(u_k n_k)^2 d:x' @ ns, degree=2)
  sqr += domain.boundary['load'].integral('(u_k n_k - Δuload)^2 d:x' @ ns, degree=2)
  cons = solver.optimize('lhs', sqr, droptol=1e-15)

  # Incremental-iterative solution procedure
  # ========================================
  # We initialize the solution vector for the first load step `lhs` and solution vector
  # of the previous load step `lhs0`. Note that, in order to construct a predictor step
  # for the first load step, we define the previous load step state as the solution
  # vector corresponding to a negative elastic loading step.

  lhs0 = -solver.solve_linear('lhs', domain.integral('basis_ni,j C_ijkl ε_kl d:x' @ ns, degree=2), constrain=cons)
  lhs  = numpy.zeros_like(lhs0)
  εp0  = numpy.zeros((gauss.npoints,)+ns.εp0.shape)
  κ0   = numpy.zeros((gauss.npoints,)+ns.κ0.shape)

  # To store the force-dispalcement data we initialize an empty data array with the
  # inital state solution substituted in the first row.
  fddata      = numpy.empty(shape=(nsteps+1,2))
  fddata[:]   = numpy.nan
  fddata[0,:] = 0

  # Load step incrementation
  # ------------------------
  with treelog.iter.fraction('step', range(nsteps)) as counter:
    for step in counter:

      # The solution of the previous load step is set to `lhs0`, and the Newton solution
      # procedure is initialized by extrapolation of the state vector:
      lhs_init = lhs + (lhs-lhs0)
      lhs0     = lhs

      # The non-linear system of equations is solved for `lhs` using Newton iterations,
      # where the `step` variable is used to scale the incremental constraints.
      lhs = solver.newton(target='lhs', residual=res, constrain=cons*step, lhs0=lhs_init, arguments={'lhs0':lhs0,'εp0':εp0,'κ0':κ0}).solve(tol=1e-6)

      # The computed solution is post-processed in the form of a loading curve - which
      # plots the normalized mean stress versus the maximum 'ε_{yy}' strain
      # component - and a contour plot showing the 'σ_{yy}' stress component on a
      # deformed mesh. Note that since the stresses are defined in the integration points
      # only, a post-processing step is involved that transfers the stress information to
      # the nodal points.
      εyymax = gauss.eval(ns.ε[1,1], arguments=dict(lhs=lhs)).max()

      basis = domain.basis('std', degree=1)
      bw, b = domain.integrate([basis * ns.σ[1,1] * function.J(geom), basis * function.J(geom)], degree=2, arguments=dict(lhs=lhs,lhs0=lhs0,εp0=εp0,κ0=κ0))
      σyy = basis.dot(bw / b)

      uyload, σyyload = domain.boundary['load'].integrate(['u_1 d:x'@ns,σyy * function.J(geom)], degree=2, arguments=dict(lhs=lhs,εp0=εp0,κ0=κ0))
      uyload /= Wdomain
      σyyload /= Wdomain
      fddata[step,0] = (E*εyymax)/σyield
      fddata[step,1] = (σyyload*2)/σyield

      with export.mplfigure('forcedisp.png') as fig:
        ax = fig.add_subplot(111, xlabel=r'${E \cdot {\rm max}(\varepsilon_{yy})}/{\sigma_{\rm yield}}$', ylabel=r'${\sigma_{\rm mean}}/{\sigma_{\rm yield}}$')
        if referencedata:
          data = numpy.genfromtxt(pathlib.Path(__file__).parent/referencedata, delimiter=',', skip_header=1)
          ax.plot(data[:,0], data[:,1], 'r:', label='Reference')
          ax.legend()
        ax.plot(fddata[:,0], fddata[:,1], 'o-', label='Nutils')
        ax.grid()

      bezier = domain.sample('bezier', 3)
      points, uvals, σyyvals = bezier.eval(['(x_i + 25 u_i)' @ ns, ns.u, σyy], arguments=dict(lhs=lhs))
      with export.mplfigure('stress.png') as fig:
        ax = fig.add_subplot(111, aspect='equal', xlabel=r'$x$ [mm]', ylabel=r'$y$ [mm]')
        im = ax.tripcolor(points[:,0]/unit('mm'), points[:,1]/unit('mm'), bezier.tri, σyyvals/unit('MPa'), shading='gouraud', cmap='jet')
        ax.add_collection(collections.LineCollection(points.take(bezier.hull, axis=0), colors='k', linewidths=.1))
        ax.autoscale(enable=True, axis='both', tight=True)
        cb = fig.colorbar(im)
        im.set_clim(0, 1.2*σyield)
        cb.set_label(r'$σ_{yy}$ [MPa]')

      # Load step convergence
      # ---------------------
      # At the end of the loading step, the plastic strain state and history parameter are updated,
      # where use if made of the strain hardening relation for the history variable:
      #
      #   Δκ = √(Δεp_ij Δεp_ij)

      Δεp = ns.εp-ns.εp0
      Δκ  = function.sqrt((Δεp*Δεp).sum((0,1)))
      κ0  = gauss.eval(ns.κ0+Δκ, arguments=dict(lhs0=lhs0,lhs=lhs,εp0=εp0,κ0=κ0))
      εp0 = gauss.eval(ns.εp, arguments=dict(lhs0=lhs0,lhs=lhs,εp0=εp0,κ0=κ0))
Exemple #29
0
def main(
    L: 'domain size' = 4.,
    R: 'hole radius' = 1.,
    E: "young's modulus" = 1e5,
    nu: "poisson's ratio" = 0.3,
    T: 'far field traction' = 10,
    nr: 'number of h-refinements' = 2,
    figures: 'create figures' = True,
  ):

  ns = function.Namespace()
  ns.lmbda = E*nu/(1-nu**2)
  ns.mu = .5*E/(1+nu)

  # create the coarsest level parameter domain
  domain0, geometry = mesh.rectilinear( [1,2] )

  # create the second-order B-spline basis over the coarsest domain
  ns.bsplinebasis = domain0.basis('spline', degree=2)
  ns.controlweights = 1,.5+.5/2**.5,.5+.5/2**.5,1,1,1,1,1,1,1,1,1
  ns.weightfunc = 'bsplinebasis_n controlweights_n'
  ns.nurbsbasis = ns.bsplinebasis * ns.controlweights / ns.weightfunc

  # create the isogeometric map
  ns.controlpoints = [0,R],[R*(1-2**.5),R],[-R,R*(2**.5-1)],[-R,0],[0,.5*(R+L)],[-.15*(R+L),.5*(R+L)],[-.5*(R+L),.15*(R*L)],[-.5*(R+L),0],[0,L],[-L,L],[-L,L],[-L,0]
  ns.x_i = 'nurbsbasis_n controlpoints_ni'

  # create the computational domain by h-refinement
  domain = domain0.refine(nr)

  # create the second-order B-spline basis over the refined domain
  ns.bsplinebasis = domain.basis('spline', degree=2)
  sqr = domain.integral('(bsplinebasis_n ?lhs_n - weightfunc)^2' @ ns, geometry=ns.x, degree=9)
  ns.controlweights = solver.optimize('lhs', sqr)
  ns.nurbsbasis = ns.bsplinebasis * ns.controlweights / ns.weightfunc

  # prepare the displacement field
  ns.ubasis = ns.nurbsbasis.vector(2)
  ns.u_i = 'ubasis_ni ?lhs_n'
  ns.strain_ij = '(u_i,j + u_j,i) / 2'
  ns.stress_ij = 'lmbda strain_kk δ_ij + 2 mu strain_ij'

  # construct the exact solution
  ns.r2 = 'x_k x_k'
  ns.R2 = R**2
  ns.T = T
  ns.k = (3-nu) / (1+nu) # plane stress parameter
  ns.uexact_i = '''(T / 4 mu) <x_0 ((k + 1) / 2 + (1 + k) R2 / r2 + (1 - R2 / r2) (x_0^2 - 3 x_1^2) R2 / r2^2),
                               x_1 ((k - 3) / 2 + (1 - k) R2 / r2 + (1 - R2 / r2) (3 x_0^2 - x_1^2) R2 / r2^2)>_i'''
  ns.strainexact_ij = '(uexact_i,j + uexact_j,i) / 2'
  ns.stressexact_ij = 'lmbda strainexact_kk δ_ij + 2 mu strainexact_ij'

  # define the linear and bilinear forms
  res = domain.integral('ubasis_ni,j stress_ij' @ ns, geometry=ns.x, degree=9)
  res -= domain.boundary['right'].integral('ubasis_ni stressexact_ij n_j' @ ns, geometry=ns.x, degree=9)

  # compute the constraints vector for the symmetry conditions
  sqr = domain.boundary['top,bottom'].integral('(u_i n_i)^2' @ ns, degree=9)
  cons = solver.optimize('lhs', sqr, droptol=1e-15)

  # solve the system of equations
  lhs = solver.solve_linear('lhs', res, constrain=cons)
  ns = ns(lhs=lhs)

  # post-processing
  if figures:
    geom, stressxx = domain.simplex.elem_eval([ns.x, 'stress_00' @ ns], ischeme='bezier8', separate=True)
    with plot.PyPlot( 'solution', index=nr ) as plt:
      plt.mesh( geom, stressxx )
      plt.colorbar()

  # compute the L2-norm of the error in the stress
  err = domain.integrate('(?dstress_ij ?dstress_ij)(dstress_ij = stress_ij - stressexact_ij)' @ ns, geometry=ns.x, ischeme='gauss9')**.5

  # compute the mesh parameter (maximum physical distance between knots)
  hmax = max(numpy.linalg.norm(v[:,numpy.newaxis]-v, axis=2).max() for v in domain.elem_eval(ns.x, ischeme='bezier2', separate=True))

  return err, hmax
Exemple #30
0
def main(nelems: int, etype: str, btype: str, degree: int):
    '''
  Laplace problem on a unit square.

  .. arguments::

     nelems [10]
       Number of elements along edge.
     etype [square]
       Type of elements (square/triangle/mixed).
     btype [std]
       Type of basis function (std/spline), availability depending on the
       selected element type.
     degree [1]
       Polynomial degree.
  '''

    # A unit square domain is created by calling the
    # :func:`nutils.mesh.unitsquare` mesh generator, with the number of elements
    # along an edge as the first argument, and the type of elements ("square",
    # "triangle", or "mixed") as the second. The result is a topology object
    # ``domain`` and a vectored valued geometry function ``geom``.

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

    # To be able to write index based tensor contractions, we need to bundle all
    # relevant functions together in a namespace. Here we add the geometry ``x``,
    # a scalar ``basis``, and the solution ``u``. The latter is formed by
    # contracting the basis with a to-be-determined solution vector ``?lhs``.

    ns = function.Namespace()
    ns.x = geom
    ns.basis = domain.basis(btype, degree=degree)
    ns.u = 'basis_n ?lhs_n'

    # We are now ready to implement the Laplace equation. In weak form, the
    # solution is a scalar field :math:`u` for which:
    #
    # .. math:: ∀ v: ∫_Ω \frac{dv}{dx_i} \frac{du}{dx_i} - ∫_{Γ_n} v f = 0.
    #
    # By linearity the test function :math:`v` can be replaced by the basis that
    # spans its space. The result is an integral ``res`` that evaluates to a
    # vector matching the size of the function space.

    res = domain.integral('d(basis_n, x_i) d(u, x_i) J(x)' @ ns,
                          degree=degree * 2)
    res -= domain.boundary['right'].integral(
        'basis_n cos(1) cosh(x_1) J(x)' @ ns, degree=degree * 2)

    # The Dirichlet constraints are set by finding the coefficients that minimize
    # the error:
    #
    # .. math:: \min_u ∫_{\Gamma_d} (u - u_d)^2
    #
    # The resulting ``cons`` array holds numerical values for all the entries of
    # ``?lhs`` that contribute (up to ``droptol``) to the minimization problem.
    # All remaining entries are set to ``NaN``, signifying that these degrees of
    # freedom are unconstrained.

    sqr = domain.boundary['left'].integral('u^2 J(x)' @ ns, degree=degree * 2)
    sqr += domain.boundary['top'].integral(
        '(u - cosh(1) sin(x_0))^2 J(x)' @ ns, degree=degree * 2)
    cons = solver.optimize('lhs', sqr, droptol=1e-15)

    # The unconstrained entries of ``?lhs`` are to be determined such that the
    # residual vector evaluates to zero in the corresponding entries. This step
    # involves a linearization of ``res``, resulting in a jacobian matrix and
    # right hand side vector that are subsequently assembled and solved. The
    # resulting ``lhs`` array matches ``cons`` in the constrained entries.

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

    # Once all entries of ``?lhs`` are establised, the corresponding solution can
    # be vizualised by sampling values of ``ns.u`` along with physical
    # coordinates ``ns.x``, with the solution vector provided via the
    # ``arguments`` dictionary. The sample members ``tri`` and ``hull`` provide
    # additional inter-point information required for drawing the mesh and
    # element outlines.

    bezier = domain.sample('bezier', 9)
    x, u = bezier.eval(['x', 'u'] @ ns, lhs=lhs)
    export.triplot('solution.png', x, u, tri=bezier.tri, hull=bezier.hull)

    # To confirm that our computation is correct, we use our knowledge of the
    # analytical solution to evaluate the L2-error of the discrete result.

    err = domain.integral('(u - sin(x_0) cosh(x_1))^2 J(x)' @ ns,
                          degree=degree * 2).eval(lhs=lhs)**.5
    treelog.user('L2 error: {:.2e}'.format(err))

    return cons, lhs, err
Exemple #31
0
def main(
    L: 'domain size' = 4.,
    R: 'hole radius' = 1.,
    E: "young's modulus" = 1e5,
    nu: "poisson's ratio" = 0.3,
    T: 'far field traction' = 10,
    nr: 'number of h-refinements' = 2,
    withplots: 'create plots' = True,
):

    ns = function.Namespace()
    ns.lmbda = E * nu / (1 - nu**2)
    ns.mu = .5 * E / (1 + nu)

    # create the coarsest level parameter domain
    domain0, geometry = mesh.rectilinear([1, 2])

    # create the second-order B-spline basis over the coarsest domain
    ns.bsplinebasis = domain0.basis('spline', degree=2)
    ns.controlweights = 1, .5 + .5 / 2**.5, .5 + .5 / 2**.5, 1, 1, 1, 1, 1, 1, 1, 1, 1
    ns.weightfunc = 'bsplinebasis_n controlweights_n'
    ns.nurbsbasis = ns.bsplinebasis * ns.controlweights / ns.weightfunc

    # create the isogeometric map
    ns.controlpoints = [0, R], [R * (1 - 2**.5), R], [-R, R * (2**.5 - 1)], [
        -R, 0
    ], [0, .5 * (R + L)], [-.15 * (R + L),
                           .5 * (R + L)], [-.5 * (R + L), .15 * (R * L)
                                           ], [-.5 * (R + L),
                                               0], [0, L], [-L,
                                                            L], [-L,
                                                                 L], [-L, 0]
    ns.x_i = 'nurbsbasis_n controlpoints_ni'

    # create the computational domain by h-refinement
    domain = domain0.refine(nr)

    # create the second-order B-spline basis over the refined domain
    ns.bsplinebasis = domain.basis('spline', degree=2)
    sqr = domain.integral('(bsplinebasis_n ?lhs_n - weightfunc)^2' @ ns,
                          geometry=ns.x,
                          degree=9)
    ns.controlweights = solver.optimize('lhs', sqr)
    ns.nurbsbasis = ns.bsplinebasis * ns.controlweights / ns.weightfunc

    # prepare the displacement field
    ns.ubasis = ns.nurbsbasis.vector(2)
    ns.u_i = 'ubasis_ni ?lhs_n'
    ns.strain_ij = '(u_i,j + u_j,i) / 2'
    ns.stress_ij = 'lmbda strain_kk δ_ij + 2 mu strain_ij'

    # construct the exact solution
    ns.r2 = 'x_k x_k'
    ns.R2 = R**2
    ns.T = T
    ns.k = (3 - nu) / (1 + nu)  # plane stress parameter
    ns.uexact_i = '''(T / 4 mu) <x_0 ((k + 1) / 2 + (1 + k) R2 / r2 + (1 - R2 / r2) (x_0^2 - 3 x_1^2) R2 / r2^2),
                               x_1 ((k - 3) / 2 + (1 - k) R2 / r2 + (1 - R2 / r2) (3 x_0^2 - x_1^2) R2 / r2^2)>_i'''
    ns.strainexact_ij = '(uexact_i,j + uexact_j,i) / 2'
    ns.stressexact_ij = 'lmbda strainexact_kk δ_ij + 2 mu strainexact_ij'

    # define the linear and bilinear forms
    res = domain.integral('ubasis_ni,j stress_ij' @ ns,
                          geometry=ns.x,
                          degree=9)
    res -= domain.boundary['right'].integral(
        'ubasis_ni stressexact_ij n_j' @ ns, geometry=ns.x, degree=9)

    # compute the constraints vector for the symmetry conditions
    sqr = domain.boundary['top,bottom'].integral('(u_i n_i)^2' @ ns, degree=9)
    cons = solver.optimize('lhs', sqr, droptol=1e-15)

    # solve the system of equations
    lhs = solver.solve_linear('lhs', res, constrain=cons)
    ns |= dict(lhs=lhs)

    # post-processing
    if withplots:
        geom, stressxx = domain.simplex.elem_eval([ns.x, 'stress_00' @ ns],
                                                  ischeme='bezier8',
                                                  separate=True)
        with plot.PyPlot('solution', index=nr) as plt:
            plt.mesh(geom, stressxx)
            plt.colorbar()

    # compute the L2-norm of the error in the stress
    err = domain.integrate(
        '?dstress_ij ?dstress_ij | ?dstress_ij = stress_ij - stressexact_ij'
        @ ns,
        geometry=ns.x,
        ischeme='gauss9')**.5

    # compute the mesh parameter (maximum physical distance between knots)
    hmax = max(
        numpy.linalg.norm(v[:, numpy.newaxis] - v, axis=2).max()
        for v in domain.elem_eval(ns.x, ischeme='bezier2', separate=True))

    return err, hmax
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
Exemple #33
0
def main(etype: str, btype: str, degree: int, nrefine: int):
    '''
  Adaptively refined Laplace problem on an L-shaped domain.

  .. arguments::

     etype [square]
       Type of elements (square/triangle/mixed).
     btype [h-std]
       Type of basis function (h/th-std/spline), with availability depending on
       the configured element type.
     degree [2]
       Polynomial degree
     nrefine [5]
       Number of refinement steps to perform.
  '''

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

    x, y = geom - .5
    exact = (x**2 + y**2)**(1 / 3) * function.cos(
        function.arctan2(y + x, y - x) * (2 / 3))
    domain = domain.trim(exact - 1e-15, maxrefine=0)
    linreg = util.linear_regressor()

    with treelog.iter.fraction('level', range(nrefine + 1)) as lrange:
        for irefine in lrange:

            if irefine:
                refdom = domain.refined
                ns.refbasis = refdom.basis(btype, degree=degree)
                indicator = refdom.integral(
                    'd(refbasis_n, x_k) d(u, x_k) J(x)' @ ns,
                    degree=degree * 2).eval(lhs=lhs)
                indicator -= refdom.boundary.integral(
                    'refbasis_n d(u, x_k) n(x_k) J(x)' @ ns,
                    degree=degree * 2).eval(lhs=lhs)
                supp = ns.refbasis.get_support(
                    indicator**2 > numpy.mean(indicator**2))
                domain = domain.refined_by(refdom.transforms[supp])

            ns = function.Namespace()
            ns.x = geom
            ns.basis = domain.basis(btype, degree=degree)
            ns.u = 'basis_n ?lhs_n'
            ns.du = ns.u - exact

            sqr = domain.boundary['trimmed'].integral('u^2 J(x)' @ ns,
                                                      degree=degree * 2)
            cons = solver.optimize('lhs', sqr, droptol=1e-15)

            sqr = domain.boundary.integral('du^2 J(x)' @ ns, degree=7)
            cons = solver.optimize('lhs', sqr, droptol=1e-15, constrain=cons)

            res = domain.integral('d(basis_n, x_k) d(u, x_k) J(x)' @ ns,
                                  degree=degree * 2)
            lhs = solver.solve_linear('lhs', res, constrain=cons)

            ndofs = len(ns.basis)
            error = domain.integral('<du^2, sum:k(d(du, x_k)^2)>_i J(x)' @ ns,
                                    degree=7).eval(lhs=lhs)**.5
            rate, offset = linreg.add(numpy.log(len(ns.basis)),
                                      numpy.log(error))
            treelog.user(
                'ndofs: {ndofs}, L2 error: {error[0]:.2e} ({rate[0]:.2f}), H1 error: {error[1]:.2e} ({rate[1]:.2f})'
                .format(ndofs=len(ns.basis), error=error, rate=rate))

            bezier = domain.sample('bezier', 9)
            x, u, du = bezier.eval(['x', 'u', 'du'] @ ns, lhs=lhs)
            export.triplot('sol.png', x, u, tri=bezier.tri, hull=bezier.hull)
            export.triplot('err.png', x, du, tri=bezier.tri, hull=bezier.hull)

    return ndofs, error, lhs
Exemple #34
0
def neutrondiffusion(mat_flag='scatterer',
                     BC_flag='vacuum',
                     bigsquare=1,
                     smallsquare=.1,
                     degree=1,
                     basis='lagrange'):

    W = bigsquare  #width of outer box
    Wi = smallsquare  #width of inner box

    nelems = int((2 * 1 * W / Wi))

    if mat_flag == 'scatterer':
        sigt = 2
        sigs = 1.99
    elif mat_flag == 'reflector':
        sigt = 2
        sigs = 1.8
    elif mat_flag == 'absorber':
        sigt = 10
        sigs = 2
    elif mat_flag == 'air':
        sigt = .01
        sigs = .006

    topo, geom = mesh.unitsquare(nelems,
                                 'square')  #unit square centred at (0.5, 0.5)
    ns = function.Namespace()
    ns.basis = topo.basis(basis, degree=degree)

    ns.x = W * geom  #scales the unit square to our physical square
    ns.f = function.max(
        function.abs(ns.x[0] - W / 2),
        function.abs(ns.x[1] - W / 2))  #level set function for inner square

    inner, outer = function.partition(
        ns.f, Wi / 2)  #indicator function for inner square and outer square

    ns.phi = 'basis_A ?dofs_A'
    ns.SIGs = sigs * outer  #scattering cross-section
    ns.SIGt = 0.1 * inner + sigt * outer  #total cross-section
    ns.SIGa = 'SIGt - SIGs'  #absorption cross-section
    ns.D = '1 / (3 SIGt)'  #diffusion co-efficient
    ns.Q = inner  #source term

    if BC_flag == 'vacuum':
        sqr = topo.boundary.integral('(phi - 0)^2 J(x)' @ ns,
                                     degree=degree * 2)
        cons = solver.optimize(
            'dofs', sqr,
            droptol=1e-14)  #this applies the boundary condition to u

        #residual
        res = topo.integral(
            '(D basis_i,j phi_,j + SIGa basis_i phi - basis_i Q) J(x)' @ ns,
            degree=degree * 2)

        #solve for degrees of freedom
        dofs = solver.solve_linear('dofs', res, constrain=cons)

    elif BC_flag == 'reflecting':
        #residual
        res = topo.integral(
            '(D basis_i,j phi_,j + SIGa basis_i phi - basis_i Q) J(x)' @ ns,
            degree=degree * 2)

        #solve for degrees of freedom
        dofs = solver.solve_linear('dofs', res)

    #select lower triangular half of square domain. Diagonal is one of its boundaries
    triang = topo.trim('x_0 - x_1' @ ns, maxrefine=5)
    triangbnd = triang.boundary  #select boundary of lower triangle
    # eval the vertices of the boundary elements of lower triangle:
    verts = triangbnd.sample(*element.parse_legacy_ischeme("vertex")).eval(
        'x_i' @ ns)
    # now select the verts of the boundary
    diag = topology.SubsetTopology(triangbnd, [
        triangbnd.references[i] if
        (verts[2 * i][0] == verts[2 * i][1] and verts[2 * i + 1][0]
         == verts[2 * i + 1][1]) else triangbnd.references[i].empty
        for i in range(len(triangbnd.references))
    ])
    bezier_diag = diag.sample('bezier', degree + 1)
    x_diag = bezier_diag.eval('(x_0^2 + x_1^2)^(1 / 2)' @ ns)
    phi_diag = bezier_diag.eval('phi' @ ns, dofs=dofs)

    bezier_bottom = topo.boundary['bottom'].sample('bezier', degree + 1)
    x_bottom = bezier_bottom.eval('x_0' @ ns)
    phi_bottom = bezier_bottom.eval('phi' @ ns, dofs=dofs)

    fig_bottom = plt.figure(0)
    ax_bottom = fig_bottom.add_subplot(111)
    plt.plot(x_bottom, phi_bottom)
    ax_bottom.set_title('Scalar flux along bottom for %s with %s BCs' %
                        (mat_flag, BC_flag))
    ax_bottom.set_xlabel('x')
    ax_bottom.set_ylabel('$\\phi(x)$')

    fig_diag = plt.figure(1)
    ax_diag = fig_diag.add_subplot(111)
    plt.plot(x_diag, phi_diag)
    ax_diag.set_title('Scalar flux along diagonal for %s with %s BCs' %
                      (mat_flag, BC_flag))
    ax_diag.set_xlabel('$\\sqrt{x^2 + y^2}$')
    ax_diag.set_ylabel('$\\phi(x = y)$')

    return fig_bottom, fig_diag
Exemple #35
0
def main(nelems: int, etype: str, btype: str, degree: int, traction: float,
         maxrefine: int, radius: float, poisson: float):
    '''
  Horizontally loaded linear elastic plate with FCM hole.

  .. arguments::

     nelems [9]
       Number of elements along edge.
     etype [square]
       Type of elements (square/triangle/mixed).
     btype [std]
       Type of basis function (std/spline), with availability depending on the
       selected element type.
     degree [2]
       Polynomial degree.
     traction [.1]
       Far field traction (relative to Young's modulus).
     maxrefine [2]
       Number or refinement levels used for the finite cell method.
     radius [.5]
       Cut-out radius.
     poisson [.3]
       Poisson's ratio, nonnegative and strictly smaller than 1/2.
  '''

    domain0, geom = mesh.unitsquare(nelems, etype)
    domain = domain0.trim(function.norm2(geom) - radius, maxrefine=maxrefine)

    ns = function.Namespace()
    ns.x = geom
    ns.lmbda = 2 * poisson
    ns.mu = 1 - poisson
    ns.ubasis = domain.basis(btype, degree=degree).vector(2)
    ns.u_i = 'ubasis_ni ?lhs_n'
    ns.X_i = 'x_i + u_i'
    ns.strain_ij = '(d(u_i, x_j) + d(u_j, x_i)) / 2'
    ns.stress_ij = 'lmbda strain_kk δ_ij + 2 mu strain_ij'
    ns.r2 = 'x_k x_k'
    ns.R2 = radius**2 / ns.r2
    ns.k = (3 - poisson) / (1 + poisson)  # plane stress parameter
    ns.scale = traction * (1 + poisson) / 2
    ns.uexact_i = 'scale (x_i ((k + 1) (0.5 + R2) + (1 - R2) R2 (x_0^2 - 3 x_1^2) / r2) - 2 δ_i1 x_1 (1 + (k - 1 + R2) R2))'
    ns.du_i = 'u_i - uexact_i'

    sqr = domain.boundary['left,bottom'].integral('(u_i n_i)^2 J(x)' @ ns,
                                                  degree=degree * 2)
    cons = solver.optimize('lhs', sqr, droptol=1e-15)
    sqr = domain.boundary['top,right'].integral('du_k du_k J(x)' @ ns,
                                                degree=20)
    cons = solver.optimize('lhs', sqr, droptol=1e-15, constrain=cons)

    res = domain.integral('d(ubasis_ni, x_j) stress_ij J(x)' @ ns,
                          degree=degree * 2)
    lhs = solver.solve_linear('lhs', res, constrain=cons)

    bezier = domain.sample('bezier', 5)
    X, stressxx = bezier.eval(['X', 'stress_00'] @ ns, lhs=lhs)
    export.triplot('stressxx.png',
                   X,
                   stressxx,
                   tri=bezier.tri,
                   hull=bezier.hull)

    err = domain.integral('<du_k du_k, sum:ij(d(du_i, x_j)^2)>_n J(x)' @ ns,
                          degree=max(degree, 3) * 2).eval(lhs=lhs)**.5
    treelog.user('errors: L2={:.2e}, H1={:.2e}'.format(*err))

    return err, cons, lhs
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