예제 #1
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)
예제 #2
0
    def test_sparsity(self):
        topo, geom = mesh.rectilinear([6] * self.ndim)
        topo = topo.refined_by(
            set(
                map(
                    topo.transforms.index,
                    itertools.chain(topo[1:3].transforms,
                                    topo[-2:].transforms))))

        ns = function.Namespace()
        ns.x = geom
        ns.tbasis = topo.basis('th-spline',
                               degree=2,
                               truncation_tolerance=1e-14)
        ns.tnotol = topo.basis('th-spline', degree=2, truncation_tolerance=0)
        ns.hbasis = topo.basis('h-spline', degree=2)

        tA, tA_tol, hA = topo.sample('gauss', 5).integrate_sparse([
            ns.eval_ij('tbasis_i,k tbasis_j,k'),
            ns.eval_ij('tnotol_i,k tnotol_j,k'),
            ns.eval_ij('hbasis_i,k hbasis_j,k')
        ])

        tA_nnz, tA_tol_nnz, hA_nnz = self.vals[self.ndim]
        self.assertEqual(len(sparse.prune(sparse.dedup(tA))), tA_nnz)
        self.assertEqual(len(sparse.prune(sparse.dedup(tA_tol))), tA_tol_nnz)
        self.assertEqual(len(sparse.prune(sparse.dedup(hA))), hA_nnz)
예제 #3
0
 def setUp(self):
   super().setUp()
   self.ns = function.Namespace()
   self.domain, self.ns.geom = mesh.rectilinear([2,2])
   self.ns.ubasis = self.domain.basis('std', degree=1)
   self.ns.u = 'ubasis_n ?dofs_n'
   self.optimize = solver.optimize if not self.minimize else lambda *args, newtontol=0, **kwargs: solver.minimize(*args, **kwargs).solve(newtontol)
예제 #4
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
예제 #5
0
def main(nelems: int, degree: int, reynolds: float):
    '''
  Driven cavity benchmark problem using compatible spaces.

  .. arguments::

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

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

    ns = function.Namespace()
    ns.x = geom
    ns.Re = reynolds
    ns.ubasis = function.vectorize([
        domain.basis('spline',
                     degree=(degree, degree - 1),
                     removedofs=((0, -1), None)),
        domain.basis('spline',
                     degree=(degree - 1, degree),
                     removedofs=(None, (0, -1)))
    ])
    ns.pbasis = domain.basis('spline', 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'
    ns.uwall = domain.boundary.indicator('top'), 0
    ns.N = 5 * degree * nelems  # nitsche constant based on element size = 1/nelems
    ns.nitsche_ni = '(N ubasis_ni - (d(ubasis_ni, x_j) + d(ubasis_nj, x_i)) n(x_j)) / Re'

    ures = domain.integral('d(ubasis_ni, x_j) stress_ij d:x' @ ns,
                           degree=2 * degree)
    ures += domain.boundary.integral(
        '(nitsche_ni (u_i - uwall_i) - ubasis_ni stress_ij n(x_j)) d:x' @ ns,
        degree=2 * degree)
    pres = domain.integral('pbasis_n (d(u_k, x_k) + ?lm) d:x' @ ns,
                           degree=2 * degree)
    lres = domain.integral('p d:x' @ ns, degree=2 * degree)

    with treelog.context('stokes'):
        state0 = solver.solve_linear(['u', 'p', 'lm'], [ures, pres, lres])
        postprocess(domain, ns, **state0)

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

    return state0, state1
예제 #6
0
 def setUp(self):
   ns = function.Namespace()
   domain, ns.x = mesh.rectilinear([10], periodic=(0,))
   ns.basis = domain.basis('discont', degree=1)
   ns.u = 'basis_n ?dofs_n'
   ns.f = '.5 u^2'
   self.residual = domain.integral('-basis_n,0 f d:x' @ ns, degree=2)
   self.residual += domain.interfaces.integral('-[basis_n] n_0 ({f} - .5 [u] n_0) d:x' @ ns, degree=4)
   self.inertia = domain.integral('basis_n u d:x' @ ns, degree=5)
   self.lhs0 = numpy.sin(numpy.arange(len(ns.basis))) # "random" initial vector
예제 #7
0
def main(nelems: int, degree: int, reynolds: float):
    '''
  Driven cavity benchmark problem using compatible spaces.

  .. arguments::

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

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

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

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

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

    return lhs0, lhs1
예제 #8
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
예제 #9
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
예제 #10
0
 def project(self, projection):
     ns = fn.Namespace()
     for name, func in self._kwargs.items():
         setattr(ns, name, func)
     for p, (name, func) in zip(projection, self._defaults.items()):
         if p is not None:
             func = fn.matmat(p, func)
         setattr(ns, name, func)
     integrand = getattr(ns, self._evaluator)(self._code)
     domain, geom, ischeme = self.prop('domain', 'geometry', 'ischeme')
     with matrix.Numpy():
         retval = domain.integrate(integrand * fn.J(geom), ischeme=ischeme)
     return NumpyArrayIntegrand(retval)
예제 #11
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
예제 #12
0
def main(
    nelems: 'number of elements' = 20,
    degree: 'polynomial degree' = 1,
    timescale: 'time scale (timestep=timescale/nelems)' = .5,
    tol: 'solver tolerance' = 1e-5,
    ndims: 'spatial dimension' = 1,
    endtime: 'end time, 0 for no end time' = 0,
    withplots: 'create plots' = True,
):

    # construct mesh, basis
    ns = function.Namespace()
    domain, ns.x = mesh.rectilinear([numpy.linspace(0, 1, nelems + 1)] * ndims,
                                    periodic=range(ndims))
    ns.basis = domain.basis('discont', degree=degree)
    ns.u = 'basis_n ?lhs_n'

    # construct initial condition (centered gaussian)
    lhs0 = domain.project('exp(-?y_i ?y_i) | ?y_i = 5 (x_i - 0.5_i)' @ ns,
                          onto=ns.basis,
                          geometry=ns.x,
                          degree=5)

    # prepare residual
    ns.f = '.5 u^2'
    ns.C = 1
    res = domain.integral('-basis_n,0 f' @ ns, geometry=ns.x, degree=5)
    res += domain.interfaces.integral(
        '-[basis_n] n_0 ({f} - .5 C [u] n_0)' @ ns, geometry=ns.x, degree=5)
    inertia = domain.integral('basis_n u' @ ns, geometry=ns.x, degree=5)

    # prepare plotting
    makeplots = MakePlots(domain, video=withplots
                          == 'video') if withplots else lambda ns: None

    # start time stepping
    timestep = timescale / nelems
    for itime, lhs in log.enumerate(
            'timestep',
            solver.impliciteuler('lhs',
                                 res,
                                 inertia,
                                 timestep,
                                 lhs0,
                                 newtontol=tol)):
        makeplots(ns | dict(lhs=lhs))
        if endtime and itime * timestep >= endtime:
            break

    return res.eval(arguments=dict(lhs=lhs)), lhs
예제 #13
0
 def check(self, method, theta):
   ns = function.Namespace()
   topo, ns.x = mesh.rectilinear([1])
   ns.u_n = '?u_n + <0>_n'
   inertia = topo.integral('?u_n d:x' @ ns, degree=0)
   residual = topo.integral('-<1>_n sin(?t) d:x' @ ns, degree=0)
   timestep = 0.1
   udesired = numpy.array([0.])
   uactualiter = iter(method(target='u', residual=residual, inertia=inertia, timestep=timestep, lhs0=udesired, timetarget='t'))
   for i in range(5):
     with self.subTest(i=i):
       uactual = next(uactualiter)
       self.assertAllAlmostEqual(uactual, udesired)
       udesired += timestep*(theta*numpy.sin((i+1)*timestep)+(1-theta)*numpy.sin(i*timestep))
예제 #14
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
예제 #15
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
예제 #16
0
def main(
    nelems: 'number of elements' = 12,
    viscosity: 'fluid viscosity' = 1e-3,
    density: 'fluid density' = 1,
    degree: 'polynomial degree' = 2,
    warp: 'warp domain (downward bend)' = False,
    figures: 'create figures' = True,
):

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

    # create namespace
    ns = function.Namespace()
    ns.viscosity = viscosity
    ns.density = density

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

    # construct bases
    ns.uxbasis, ns.uybasis, ns.pbasis, ns.lbasis = function.chain([
        domain.basis('spline',
                     degree=(degree + 1, degree),
                     removedofs=((0, -1), None)),
        domain.basis('spline',
                     degree=(degree, degree + 1),
                     removedofs=(None, (0, -1))),
        domain.basis('spline', degree=degree),
        [1],  # lagrange multiplier
    ])
    ns.ubasis_ni = '<uxbasis_n, uybasis_n>_i'

    # construct geometry
    if not warp:
        ns.x = ns.x0
    else:
        xi, eta = ns.x0
        ns.x = (eta + 2) * function.rotmat(xi * .4)[:, 1] - (
            0, 2)  # slight downward bend
        ns.J_ij = 'x_i,x0_j'
        ns.detJ = function.determinant(ns.J)
        ns.ubasis_ni = 'ubasis_nj J_ij / detJ'  # piola transform
        ns.pbasis_n = 'pbasis_n / detJ'

    # populate namespace
    ns.u_i = 'ubasis_ni ?lhs_n'
    ns.p = 'pbasis_n ?lhs_n'
    ns.l = 'lbasis_n ?lhs_n'
    ns.sigma_ij = 'viscosity (u_i,j + u_j,i) - p δ_ij'
    ns.c = 5 * (degree + 1) / domain.boundary.elem_eval(
        1, geometry=ns.x, ischeme='gauss2', asfunction=True)
    ns.nietzsche_ni = 'viscosity (c ubasis_ni - (ubasis_ni,j + ubasis_nj,i) n_j)'
    ns.top = domain.boundary.indicator('top')
    ns.utop_i = 'top <n_1, -n_0>_i'

    # solve stokes flow
    res = domain.integral(
        'ubasis_ni,j sigma_ij + pbasis_n (u_k,k + l) + lbasis_n p' @ ns,
        geometry=ns.x,
        degree=2 * (degree + 1))
    res += domain.boundary.integral('nietzsche_ni (u_i - utop_i)' @ ns,
                                    geometry=ns.x,
                                    degree=2 * (degree + 1))
    lhs0 = solver.solve_linear('lhs', res)
    if figures:
        postprocess(domain, ns(lhs=lhs0))

    # solve navier-stokes flow
    res += domain.integral('density ubasis_ni u_i,j u_j' @ ns,
                           geometry=ns.x,
                           degree=3 * (degree + 1))
    lhs1 = solver.newton('lhs', res, lhs0=lhs0).solve(tol=1e-10)
    if figures:
        postprocess(domain, ns(lhs=lhs1))

    return lhs0, lhs1
예제 #17
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
예제 #18
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
예제 #19
0
 def setUp(self):
     super().setUp()
     self.ns = function.Namespace()
     self.domain, self.ns.geom = mesh.rectilinear([2, 2])
     self.ns.ubasis = self.domain.basis('std', degree=1)
     self.ns.u = 'ubasis_n ?dofs_n'
예제 #20
0
def finitestrain_patch(bottom, right, top, left):
    from nutils import version
    if int(version[0]) != 4:
        raise ImportError(
            'Mismatching nutils version detected, only version 4 supported. Upgrade by \"pip install --upgrade nutils\"'
        )

    from nutils import mesh, function
    from nutils import _, log, solver

    # error test input
    if not (left.dimension == right.dimension == top.dimension ==
            bottom.dimension == 2):
        raise RuntimeError(
            'finitestrain_patch only supported for planar (2D) geometries')
    if left.rational or right.rational or top.rational or bottom.rational:
        raise RuntimeError(
            'finitestrain_patch not supported for rational splines')

    # these are given as a oriented loop, so make all run in positive parametric direction
    top.reverse()
    left.reverse()

    # in order to add spline surfaces, they need identical parametrization
    Curve.make_splines_identical(top, bottom)
    Curve.make_splines_identical(left, right)

    # create an initial mesh (correct corners) which we will morph into the right one
    p1 = bottom.order(0)
    p2 = left.order(0)
    p = max(p1, p2)
    linear = BSplineBasis(2)
    srf = Surface(linear, linear, [bottom[0], bottom[-1], top[0], top[-1]])
    srf.raise_order(p1 - 2, p2 - 2)
    for k in bottom.knots(0, True)[p1:-p1]:
        srf.insert_knot(k, 0)
    for k in left.knots(0, True)[p2:-p2]:
        srf.insert_knot(k, 1)

    # create computational mesh
    n1 = len(bottom)
    n2 = len(left)
    dim = left.dimension
    domain, geom = mesh.rectilinear(srf.knots())
    ns = function.Namespace()
    ns.basis = domain.basis('spline',
                            degree(srf),
                            knotmultiplicities=multiplicities(srf)).vector(2)
    ns.phi = domain.basis('spline',
                          degree(srf),
                          knotmultiplicities=multiplicities(srf))
    ns.eye = np.array([[1, 0], [0, 1]])
    ns.cp = controlpoints(srf)
    ns.x_i = 'cp_ni phi_n'
    ns.lmbda = 1
    ns.mu = 1

    # add total boundary conditions
    # for hard problems these will be taken in steps and multiplied by dt every
    # time (quasi-static iterations)
    constraints = np.array([[[np.nan] * n2] * n1] * dim)
    for d in range(dim):
        constraints[d, 0, :] = (left[:, d] - srf[0, :, d])
        constraints[d, -1, :] = (right[:, d] - srf[-1, :, d])
        constraints[d, :, 0] = (bottom[:, d] - srf[:, 0, d])
        constraints[d, :, -1] = (top[:, d] - srf[:, -1, d])
    # TODO: Take a close look at the logic below

    # in order to iterate, we let t0=0 be current configuration and t1=1 our target configuration
    # if solver divergeces (too large deformation), we will try with dt=0.5. If this still
    # fails we will resort to dt=0.25 until a suitable small iterations size have been found

    # dt = 1
    # t0 = 0
    # t1 = 1
    # while t0 < 1:
    # dt = t1-t0
    n = 10
    dt = 1 / n
    for i in range(n):
        # print(' ==== Quasi-static '+str(t0*100)+'-'+str(t1*100)+' % ====')
        print(' ==== Quasi-static ' + str(i / (n - 1) * 100) + ' % ====')

        # define the non-linear finite strain problem formulation
        ns.cp = np.reshape(srf[:, :, :].swapaxes(0, 1), (n1 * n2, dim),
                           order='F')
        ns.x_i = 'cp_ni phi_n'  # geometric mapping (reference geometry)
        ns.u_i = 'basis_ki ?w_k'  # displacement (unknown coefficients w_k)
        ns.X_i = 'x_i + u_i'  # displaced geometry
        ns.strain_ij = '.5 (u_i,j + u_j,i + u_k,i u_k,j)'
        ns.stress_ij = 'lmbda strain_kk eye_ij + 2 mu strain_ij'

        # try:
        residual = domain.integral(ns.eval_n('stress_ij basis_ni,j d:X'),
                                   degree=2 * p)
        cons = np.ndarray.flatten(constraints * dt, order='C')
        lhs = solver.newton('w', residual, constrain=cons).solve(
            tol=state.controlpoint_absolute_tolerance, maxiter=8)

        # store the results on a splipy object and continue
        geom = lhs.reshape((n2, n1, dim), order='F')
        srf[:, :, :] += geom.swapaxes(0, 1)

        # t0 += dt
        # t1 = 1
        # except solver.SolverError: # newton method fail to converge, try a smaller step length 'dt'
        # t1 = (t1+t0)/2
    return srf
예제 #21
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
예제 #22
0
def main(
    nelems: 'number of elements' = 12,
    viscosity: 'fluid viscosity' = 1e-2,
    density: 'fluid density' = 1,
    tol: 'solver tolerance' = 1e-12,
    rotation: 'cylinder rotation speed' = 0,
    timestep: 'time step' = 1/24,
    maxradius: 'approximate domain size' = 25,
    tmax: 'end time' = numpy.inf,
    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
예제 #23
0
class NutilsDelayedIntegrand(Integrand):

    optimized = False

    def __init__(self, code, indices, variables, **kwargs):
        super().__init__()
        self._code = code
        self._defaults = OrderedDict([(name, kwargs[name]) for name in variables])
        self._kwargs = {name: func for name, func in kwargs.items() if name not in variables}
        self._evaluator = 'eval_' + indices

        self._kwargs, self._arg_shapes = {}, {}
        self.update_kwargs({k: v for k, v in kwargs.items() if k not in variables})

        if code is not None:
            self.shape = self._integrand().shape
            self.ndim = len(self.shape)

    def update_kwargs(self, kwargs):
        for name, func in kwargs.items():
            if isinstance(func, tuple):
                func, shape = func
                self._arg_shapes[name] = shape
            self._kwargs[name] = func

    def add(self, code, **kwargs):
        self.update_kwargs(kwargs)
        if self._code is None:
            self._code = f'({code})'
            self.shape = self._integrand().shape
            self.ndim = len(self.shape)
        else:
            self._code = f'{self._code} + ({code})'

    def write(self, group, name):
        sub = group.require_group(name)
        data = {key: getattr(self, key) for key in ['_code', '_defaults', '_kwargs', '_evaluator', 'shape']}
        util.to_dataset(data, sub, 'data')
        sub.attrs['type'] = 'NutilsDelayedIntegrand'
        self.write_props(sub)
        return sub

    @staticmethod
    def read(group):
        retval = NutilsDelayedIntegrand.__new__(NutilsDelayedIntegrand)
        data = util.from_dataset(group['data'])
        retval.__dict__.update(data)
        retval.ndim = len(retval.shape)
        retval.read_props(group)
        return retval

    def _integrand(self, contraction=None, mu=None, case=None):
        if contraction is None:
            contraction = (None,) * len(self._defaults)
        ns = fn.Namespace()
        for name, func in self._kwargs.items():
            if isinstance(func, fn.Array):
                setattr(ns, name, func)
            elif mu is not None and case is not None:
                assert callable(getattr(case, func))
                setattr(ns, name, getattr(case, func)(mu))
            else:
                # This code path should ONLY be used for inferring the shape of the integrand
                assert not hasattr(self, 'shape')
                setattr(ns, name, fn.zeros(self._arg_shapes[name]))
예제 #24
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()
예제 #25
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))
예제 #26
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
예제 #27
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
예제 #28
0
def elasticity_patch(bottom, right, top, left):
    from nutils import version
    if int(version[0]) != 4:
        raise ImportError(
            'Mismatching nutils version detected, only version 4 supported. Upgrade by \"pip install --upgrade nutils\"'
        )

    from nutils import mesh, function, solver
    from nutils import _, log

    # error test input
    if not (left.dimension == right.dimension == top.dimension ==
            bottom.dimension == 2):
        raise RuntimeError(
            'elasticity_patch only supported for planar (2D) geometries')
    if left.rational or right.rational or top.rational or bottom.rational:
        raise RuntimeError(
            'elasticity_patch not supported for rational splines')

    # these are given as a oriented loop, so make all run in positive parametric direction
    top.reverse()
    left.reverse()

    # in order to add spline surfaces, they need identical parametrization
    Curve.make_splines_identical(top, bottom)
    Curve.make_splines_identical(left, right)

    # create computational (nutils) mesh
    p1 = bottom.order(0)
    p2 = left.order(0)
    n1 = len(bottom)
    n2 = len(left)
    dim = left.dimension
    k1 = bottom.knots(0)
    k2 = left.knots(0)
    m1 = [bottom.order(0) - bottom.continuity(k) - 1 for k in k1]
    m2 = [left.order(0) - left.continuity(k) - 1 for k in k2]
    domain, geom = mesh.rectilinear([k1, k2])

    # assemble system matrix
    ns = function.Namespace()
    ns.x = geom
    ns.basis = domain.basis('spline',
                            degree=[p1 - 1, p2 - 1],
                            knotmultiplicities=[m1, m2]).vector(2)
    ns.eye = np.array([[1, 0], [0, 1]])
    ns.lmbda = 1
    ns.mu = .3
    # ns.u_i = 'basis_ni ?lhs_n'
    # ns.strain_ij = '(u_i,j + u_j,i) / 2'
    # ns.stress_ij = 'lmbda strain_kk eye_ij + 2 mu strain_ij'
    ns.strain_nij = '(basis_ni,j + basis_nj,i) / 2'
    ns.stress_nij = 'lmbda strain_nkk eye_ij + 2 mu strain_nij'

    # construct matrix and right hand-side
    matrix = domain.integrate(ns.eval_nm('strain_nij stress_mij d:x'),
                              ischeme='gauss' + str(max(p1, p2) + 1))
    rhs = np.zeros((n1 * n2 * dim))

    # add boundary conditions
    constraints = np.array([[[np.nan] * n2] * n1] * dim)
    for d in range(dim):
        constraints[d, 0, :] = left[:, d]
        constraints[d, -1, :] = right[:, d]
        constraints[d, :, 0] = bottom[:, d]
        constraints[d, :, -1] = top[:, d]

    # solve system
    lhs = matrix.solve(rhs,
                       constrain=np.ndarray.flatten(constraints, order='C'))

    # rewrap results into splipy datastructures
    controlpoints = np.reshape(lhs, (dim, n1, n2), order='C')
    controlpoints = controlpoints.swapaxes(0, 2)
    controlpoints = controlpoints.swapaxes(0, 1)

    return Surface(bottom.bases[0],
                   left.bases[0],
                   controlpoints,
                   bottom.rational,
                   raw=True)
예제 #29
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
예제 #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