Пример #1
0
    def solve(self, *args, print_feval=True, **kwargs):

        feval = self._feval

        if 'init' not in kwargs:
            kwargs['init'] = self.init()

        # add an empty dict ``jac_options`` if it doesn't already exist
        kwargs.setdefault('jac_options', {})

        # this value is chosen heuristically
        kwargs['jac_options'].setdefault('inner_atol', 1e-12)

        ret = root(self, *args, **kwargs)

        if print_feval:
            feval = self._feval - feval
            entries_computed = 2 * feval * self._N
            entries_jacobian = 8 * self.M.nnz
            log.info(
                'Reached convergence after {} function evaluations.'.format(
                    feval))
            log.info(
                'This corresponds to {} evaluations of the Jacobian.'.format(
                    entries_computed / entries_jacobian))

        # discard the auxilliary variables from the solution
        return ret[-len(self._g.dofindices):]
def newton(f, x0, maxiter, restol, lintol):
    '''find x such that f(x) = 0, starting at x0'''

    for i in log.range('newton', maxiter + 1):
        if i > 0:
            # solve system linearised around `x`, solution in `direction`
            J = f(x, jacobian=True)
            direction = -J.solve(residual, tol=lintol)
            xprev = x
            residual_norm_prev = residual_norm
            # line search, stop if there is convergence w.r.t. iteration `i-1`
            for j in range(4):
                x = xprev + (0.5)**j * direction
                residual = f(x)
                residual_norm = numpy.linalg.norm(residual, 2)
                if residual_norm < residual_norm_prev:
                    break
                # else: no convergence, repeat and reduce step size by one half
            else:
                log.warning('divergence')
        else:
            # before first iteration, compute initial residual
            x = x0
            residual = f(x)
            residual_norm = numpy.linalg.norm(residual, 2)
        log.info('residual norm: {:.5e}'.format(residual_norm))
        if residual_norm < restol:
            break
    else:
        raise ValueError(
            'newton did not converge in {} iterations'.format(maxiter))
    return x
Пример #3
0
    def residual(self, c):

        g11, g12, g22 = self.metric(c)
        ret = (self._weights * (g11**2 + 2 * g12**2 + g22**2)).sum()

        log.info(ret)

        return ret
Пример #4
0
def smoothen_vector(vec0,
                    dt,
                    method='finite_difference',
                    stop={'T': 0.01},
                    miniter=10):
    """
        Smoothen vec[1: -1] using `method' until stopping criterion has been reached,
        while vec[0] and vec[-1] are held fixed.

        Parameters
        ----------
        vec0 : to-be-smoothened vector
        dt : (initial) timestep, is reduced if the convergence criterion is reached
             before #iterations >= ``miniter``
        method : smoothing method
        stop = {
                'T': (finite difference) if t > T, terminate
                'maxiter': (finite difference) if i > maxiter, terminate
                'vec': if not stop[ 'vec' ]( vec ) terminate
               }
        miniter : minimum number of iterations
    """

    t, i = 0, 0
    vec = vec0.copy()
    d = ChainMap(stop, {'T': np.inf, 'maxiter': 100, 'vec': lambda x: True})
    if method == 'finite_difference':
        N = len(vec)
        dx = 1 / (N - 1)
        fac = dt / (dx**2)
        A = sparse.diags([(-fac * np.ones(N - 2)).tolist() + [0], [1] +
                          ((1 + 2 * fac) * np.ones(N - 2)).tolist() + [1],
                          [0] + (-fac * np.ones(N - 2)).tolist()],
                         [-1, 0, 1]).tocsc()
        A = sparse.linalg.splu(A)
        while True:
            if not all([t < d['T'], i < d['maxiter'], d['vec'](vec)]):
                if i <= miniter:  # timestep too big
                    log.info('Initial timestep too big, reducing to {}'.format(
                        dt / 10))
                    return smoothen_vector(vec0,
                                           dt / 10,
                                           method=method,
                                           stop=stop)
                break
            vec = A.solve(vec)
            t += dt
            i += 1
    else:
        raise "Unknown method '{}'".format(method)

    if d['vec'](vec):
        log.warning('Failed to reach the termination criterion')
    else:
        log.info('Criterion reached at t={} in {} iterations'.format(t, i))

    return vec
Пример #5
0
 def test_badarg(self):
     status, output = self._cli(
         '--bla') if self.method == 'run' else self._cli(funcname='bla')
     with self.subTest('argparse'):
         log.info(output)
         self.assertNotIn('all OK', output)
     with self.subTest('exitstatus'):
         self.assertIsNotNone(status)
         self.assertEqual(status.code, 2)
Пример #6
0
    def constraint(self, c):

        g = self._g

        if self._absc is None:
            raise AssertionError('No constraints set.')

        log.info('Computing constraint ...')
        ret = constraint(self, *self._absc, c)
        log.info('Completed')

        return ret
Пример #7
0
    def __new__(cls, g, order, f=None, **kwargs):
        '''
            If the control mapping ``f`` is None return an instantiation
            of standard EGG.
            Else, return an instantiation of this class.
        '''

        if f is None:
            log.info('No control mapping passed, proceeding with standard EGG')
            return Elliptic(g, order, **kwargs)

        return super(EllipticControl, cls).__new__(cls)
Пример #8
0
 def backup(self):     # Create logfiles and backups
     today = datetime.date.today()
     self.dir = '/home/niki/Documents/1_Master/0_Master_thesis/3_code/Auto/'+str(today) + '/'
     srcfile1 = '/home/niki/Documents/1_Master/0_Master_thesis/3_code/'+str(os.path.basename(__file__))        
     srcfile2 = '/home/niki/Documents/1_Master/0_Master_thesis/3_code/pre_post_lib.py' 
     srcfile3 = '/home/niki/Documents/1_Master/0_Master_thesis/3_code/IGA_AM_lib.py'      
     if not os.path.exists(self.dir):
         os.makedirs(self.dir)
         os.makedirs(self.dir + '2. Figures/')
     shutil.copy(srcfile1, self.dir)
     shutil.copy(srcfile2, self.dir)
     shutil.copy(srcfile3, self.dir)
     log.info('Backup made')
Пример #9
0
 def __getitem__(self, key):
     """
         Return blockshaped function evaluations (for test and trial spaces)
     """
     try:
         ret = self._w[key]
     except KeyError:
         ret = self._w_eval(key)
         log.info(key + ' has been tabularized.')
         self._w[key] = ret
     except Exception as ex:
         raise Exception('Failed with unknown exception {}.'.format(ex))
     finally:
         return ret
Пример #10
0
 def test_badvalue(self):
     status, output = self._cli('--outrootdir=' + self.outrootdir,
                                '--nopdb', '--iarg=1', '--farg=x',
                                '--sarg=1')
     with self.subTest('outdir'):
         self.assertFalse(
             os.path.isdir(os.path.join(self.outrootdir, self.scriptname)),
             'outdir directory found')
     with self.subTest('argparse'):
         log.info(output)
         self.assertNotIn('all OK', output)
     with self.subTest('exitstatus'):
         self.assertIsNotNone(status)
         self.assertEqual(status.code, 2)
Пример #11
0
def postprocess(domain, ns):

    # confirm that velocity is pointwise divergence-free
    div = domain.integrate('(u_k,k)^2' @ ns, geometry=ns.x, degree=9)**.5
    log.info('velocity divergence: {:.2e}'.format(div))

    # plot velocity field as streamlines, pressure field as contours
    x, u, p = domain.elem_eval([ns.x, ns.u, ns.p],
                               ischeme='bezier9',
                               separate=True)
    with plot.PyPlot('flow') as plt:
        tri = plt.mesh(x, mergetol=1e-5)
        plt.tricontour(tri, p, every=.01, linestyles='solid', alpha=.333)
        plt.colorbar()
        plt.streamplot(tri, u, spacing=.01, linewidth=-10, color='k', zorder=9)
Пример #12
0
 def test_good(self):
     args = [
         '--outrootdir=' + self.outrootdir, '--nopdb', '--iarg=1',
         '--farg=1', '--sarg=1'
     ]
     status, output = self._cli(*args)
     with self.subTest('outdir'):
         self.assertTrue(
             os.path.isdir(os.path.join(self.outrootdir, self.scriptname)),
             'output directory not found')
     with self.subTest('argparse'):
         log.info(output)
         self.assertIn('all OK', output)
     with self.subTest('exitstatus'):
         self.assertIsNotNone(status)
         self.assertEqual(status.code, 0)
Пример #13
0
    def __init__(self, g, order, absc=None):

        # any problems with ``g`` will be caught by the parent __init__
        super().__init__(g, order)

        self._dindices = self._g.dofindices

        # The Jacobian-Determinant is evaluated in
        # the tensor-product points of xi and eta
        # as a constraint to warrant bijectivity of the result
        if isinstance(absc, int):
            # if an integer is passed to absc, we construct
            # abscissae that correspond to and ``absc``-order
            # Gauss quadrature scheme over all elements
            absc = fsol.make_quadrature(g, absc)[0]

        self._absc = absc

        if absc is not None:
            assert all(
                all( [aux.isincreasing(x_), x_[0] >= 0, x_[-1] <= 1] )
                                                    for x_ in absc ), \
                'Invalid constraint abscissae received.'

            _bsplev = lambda i, **kwargs: sparse.csr_matrix(
                self._splev(i, self._absc, **kwargs).ravel()[:, None])

            structure = sparse.hstack([_bsplev(i) for i in range(self._N)])
            self._w_xi = sparse.hstack(
                [_bsplev(i, dx=1) for i in range(self._N)])
            self._w_eta = sparse.hstack(
                [_bsplev(i, dy=1) for i in range(self._N)])

            # sparsity structure of the constraint jacobian
            self.jacobianstructure = \
                sparse.hstack( [ structure, structure ] ).\
                tolil()[:, self._dindices ].nonzero()

        log.info('Constraint jacobian sparsity pattern stored.')

        if not (self.constraint(self._g[self._dindices]) >= 0).all():
            log.warning('''
                    Warning, the initial guess corresponding to the passed
                    GridObject is not feasible. Unless another initial guess
                    is specified, the optimizer will most likely fail.
                ''')
Пример #14
0
    def constraint_gradient(self, c, sprs=False):

        if self._absc is None:
            raise AssertionError('No constraints set.')

        d = self._g.dofindices

        log.info('Computing constraint gradient ...')

        ret = constraint_jacobian(self, *self._absc, c, self._w_xi,
                                  self._w_eta)[:, d]

        log.info('Completed')

        if not sprs:
            return ret

        return sparse.csr_matrix(ret)
Пример #15
0
    def gradient(self, c):

        log.info('Computing gradient')

        (x_xi, x_eta), (y_xi, y_eta) = self.all_fderivs(c)
        g11, g12, g22 = self.metric(c)

        mul00 = 2 * x_xi * g22
        mul01 = 2 * x_eta * g11

        mul10 = 2 * y_xi * g22
        mul11 = 2 * y_eta * g11

        arr = self.jitarray
        return \
            np.concatenate( [
                arr( mul=mul00, w='x' ) + arr( mul=mul01, w='y' ),
                arr( mul=mul10, w='x' ) + arr( mul=mul11, w='y' )
            ] )
Пример #16
0
    def printprop(self,extended = True, EXIT = False):
        t = PrettyTable(['Variable','Value','Explanation', 'Variable type'])
        sol = self.TOTAL[0]
        typecheck = sol[3]
        for item in range(0,len(self.TOTAL)):
            sol = self.TOTAL[item]
            if not typecheck == sol[3]:
                t.add_row(['      ','      ','      ','      '])
            t.add_row([sol[0], sol[1] ,sol[2], sol[3]])
            typecheck = sol[3]
        log.user(t)

#        log.Log.write(3 , t )

        if extended:
            t = PrettyTable(['What','Value','Explanation'])
            t.add_row(['Layerthickness', self.dx2/self.n ,'The thickness of the layer in [m]'])
            log.user(t)

        if EXIT:
            log.info('Exitted the script in printproperties' )
            sys.exit()
Пример #17
0
 def wrapper():
   __log__ = log.clone()
   for item in __scope__.split('.'):
     __log__.append( item )
   results0 = core.getprop('results')
   __results__ = []
   t0 = time.time()
   try:
     f()
   except:
     exc, frames = debug.exc_info()
     log.stack( 'error: {}'.format(exc), frames )
     results0.append(( __scope__, 3 ))
     if tbexplore:
       debug.explore( repr(exc), frames, '''Test package
         {!r} failed. The traceback explorer allows you to examine the failure
         state. Closing the explorer will resume testing with the next
         package.'''.format(__scope__) )
   else:
     dt = time.time() - t0
     npassed = sum( status == 0 for name, status in __results__ )
     log.info( 'passed {}/{} tests in {:.2f} seconds'.format( npassed, len(__results__), dt ) )
     results0.extend( __results__ )
Пример #18
0
def root(I, init=None, order=1, jac_options=None, **scipyargs):

    assert order in (1, 2)

    scipyargs.setdefault('verbose', True)
    scipyargs.setdefault('maxiter', 50)

    res = I.residual

    if init is None:
        init = I._g.x[I._g.dofindices]

    if jac_options is None:
        jac_options = {}

    if order == 1:
        jac = optimize.nonlin.KrylovJacobian(**jac_options)
    else:
        jac = SecondOrderKrylovJacobian(**jac_options)

    log.info('solving system with maxiter={}'.format(scipyargs['maxiter']))

    return optimize.nonlin.nonlin_solve(res, init, jacobian=jac, **scipyargs)
Пример #19
0
 def residual(self, c):
     J = self.jacdet(c)
     ret = (self._weights * J**2).sum()
     log.info(ret)
     return ret
Пример #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',
    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
Пример #21
0
def runtests():

    args = sys.argv[1:]  # command line arguments

    if 'coverage' in sys.argv[0]:
        # coverage passes the complete commandline to `sys.argv`
        # find '-m tests' and keep the tail
        m = args.index('-m')
        assert args[m + 1] == __name__
        args = args[m + 2:]

    __tbexplore__ = '--tbexplore' in args
    if __tbexplore__:
        args.remove('--tbexplore')

    if args:
        assert len(args) == 1
        whitelist = args[0].split('.')
    else:
        whitelist = []

    __richoutput__ = True
    __selfcheck__ = True
    __log__ = log._mklog()
    try:
        results = _runtests(PACKAGES, whitelist)
    except KeyboardInterrupt:
        log.info('aborted.')
        sys.exit(-1)
    except:
        exc, frames = debug.exc_info()
        log.stack('error in unit testing framework: {}'.format(exc), frames)
        log.info('crashed.')
        sys.exit(-2)

    summary = _summarize(results)
    ntests = sum(len(tests) for tests in summary.values())
    passed = summary.pop(OK, [])
    failed = summary.pop(FAILED, [])
    error = summary.pop(ERROR, [])
    pkgerror = summary.pop(PKGERROR, [])

    log.info('{}/{} tests passed.'.format(len(passed), ntests))
    if failed:
        log.info('* failures ({}):'.format(len(failed)), ', '.join(failed))
    if error:
        log.info('* errors ({}):'.format(len(error)), ', '.join(error))
    if pkgerror:
        log.info('* package failures ({}):'.format(len(pkgerror)),
                 ', '.join(pkgerror))
    if summary:
        log.info('* invalid status ({}) - this should not happen!'.format(
            len(summary)))

    sys.exit(ntests - len(passed))
Пример #22
0
def main(
    nelems: 'number of elements' = 12,
    viscosity: 'fluid viscosity' = 1e-3,
    density: 'fluid density' = 1,
    degree: 'polynomial degree' = 2,
    warp: 'warp domain (downward bend)' = False,
    tol: 'solver tolerance' = 1e-5,
    maxiter: 'maximum number if iterations, 0 for unlimited' = 0,
    withplots: 'create plots' = True,
  ):

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

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

  # construct bases
  vxbasis, vybasis, pbasis, lbasis = function.chain([
    domain.basis( 'spline', degree=(degree+1,degree), removedofs=((0,-1),None) ),
    domain.basis( 'spline', degree=(degree,degree+1), removedofs=(None,(0,-1)) ),
    domain.basis( 'spline', degree=degree ),
    [1], # lagrange multiplier
  ])
  if not warp:
    vbasis = function.stack( [ vxbasis, vybasis ], axis=1 )
  else:
    gridgeom = geom
    xi, eta = gridgeom
    geom = (eta+2) * function.rotmat(xi*.4)[:,1] - (0,2) # slight downward bend
    J = geom.grad( gridgeom )
    detJ = function.determinant( J )
    vbasis = ( vxbasis[:,_] * J[:,0] + vybasis[:,_] * J[:,1] ) / detJ # piola transform
    pbasis /= detJ
  stressbasis = (2*viscosity) * vbasis.symgrad(geom) - (pbasis)[:,_,_] * function.eye( domain.ndims )

  # construct matrices
  A = function.outer( vbasis.grad(geom), stressbasis ).sum([2,3]) \
    + function.outer( pbasis, vbasis.div(geom)+lbasis ) \
    + function.outer( lbasis, pbasis )
  Ad = function.outer( vbasis.div(geom) )
  stokesmat, divmat = domain.integrate( [ A, Ad ], geometry=geom, ischeme='gauss9' )

  # define boundary conditions
  normal = geom.normal()
  utop = function.asarray([ normal[1], -normal[0] ])
  h = domain.boundary.elem_eval( 1, geometry=geom, ischeme='gauss9', asfunction=True )
  nietzsche = (2*viscosity) * ( ((degree+1)*2.5/h) * vbasis - vbasis.symgrad(geom).dotnorm(geom) )
  stokesmat += domain.boundary.integrate( function.outer( nietzsche, vbasis ).sum(-1), geometry=geom, ischeme='gauss9' )
  rhs = domain.boundary['top'].integrate( ( nietzsche * utop ).sum(-1), geometry=geom, ischeme='gauss9' )

  # prepare plotting
  makeplots = MakePlots( domain, geom ) if withplots else lambda *args: None

  # start picard iterations
  lhs = stokesmat.solve( rhs, tol=tol, solver='cg', precon='spilu' )
  for iiter in log.count( 'picard' ):
    log.info( 'velocity divergence:', divmat.matvec(lhs).dot(lhs) )
    makeplots( vbasis.dot(lhs), pbasis.dot(lhs) )
    ugradu = ( vbasis.grad(geom) * vbasis.dot(lhs) ).sum(-1)
    convection = density * function.outer( vbasis, ugradu ).sum(-1)
    matrix = stokesmat + domain.integrate( convection, ischeme='gauss9', geometry=geom )
    lhs, info = matrix.solve( rhs, lhs0=lhs, tol=tol, info=True, precon='spilu', restart=999 )
    if iiter == maxiter-1 or info.niter == 0:
      break

  return rhs, lhs
Пример #23
0
def _run():
  __richoutput__ = True
  __selfcheck__ = True
  __log__ = log.clone()
  __results__ = []
  try:
    for package in packages:
      package()
  except KeyboardInterrupt:
    log.info( 'aborted.' )
    sys.exit( -1 )
  except:
    exc, frames = debug.exc_info()
    log.stack( 'error in unit testing framework: {}'.format(exc), frames )
    log.info( 'crashed.' )
    sys.exit( -2 )
  passed, failed, error, packagefail = results_by_status = [], [], [], []
  for name, status in __results__:
    results_by_status[status].append( name )
  log.info( '{}/{} tests passed.'.format( len(passed), len(__results__) ) )
  if failed:
    log.info( '* failures ({}):'.format(len(failed)), ', '.join( failed ) )
  if error:
    log.info( '* errors ({}):'.format(len(error)), ', '.join( error ) )
  if packagefail:
    log.info( '* package failures ({}):'.format(len(packagefail)), ', '.join( packagefail ) )
  sys.exit( len(__results__) - len(passed) )
Пример #24
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
Пример #25
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
Пример #26
0
def main(
    nelems: 'number of elements' = 20,
    epsilon: 'epsilon, 0 for automatic (based on nelems)' = 0,
    timestep: 'time step' = .01,
    maxtime: 'end time' = 1.,
    theta: 'contact angle (degrees)' = 90,
    init: 'initial condition (random/bubbles)' = 'random',
    withplots: 'create plots' = True,
):

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

    ewall = .5 * numpy.cos(theta * numpy.pi / 180)

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

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

    # define mixing energy and splitting: F' = f_p - f_n
    F = lambda c_: (.5 / epsilon**2) * (c_**2 - 1)**2
    f_p = lambda c_: (1. / epsilon**2) * 4 * c_
    f_n = lambda c_: (1. / epsilon**2) * (6 * c_ - 2 * c_**3)

    # prepare matrix
    A = function.outer( cbasis ) \
      + (timestep*epsilon**2) * function.outer( cbasis.grad(geom), mubasis.grad(geom) ).sum(-1) \
      + function.outer( mubasis, mubasis - f_p(cbasis) ) \
      - function.outer( mubasis.grad(geom), cbasis.grad(geom) ).sum(-1)
    matrix = domain.integrate(A, geometry=geom, ischeme='gauss4')

    # prepare wall energy right hand side
    rhs0 = domain.boundary.integrate(mubasis * ewall,
                                     geometry=geom,
                                     ischeme='gauss4')

    # construct initial condition
    if init == 'random':
        numpy.random.seed(0)
        c = cbasis.dot(numpy.random.normal(0, .5, cbasis.shape))
    elif init == 'bubbles':
        R1 = .25
        R2 = numpy.sqrt(.5) * R1  # area2 = .5 * area1
        c = 1 + function.tanh( (R1-function.norm2(geom-(.5+R2/numpy.sqrt(2)+.8*epsilon)))/epsilon ) \
              + function.tanh( (R2-function.norm2(geom-(.5-R1/numpy.sqrt(2)-.8*epsilon)))/epsilon )
    else:
        raise Exception('unknown init %r' % init)

    # prepare plotting
    nsteps = numeric.round(maxtime / timestep)
    makeplots = MakePlots(domain, geom, nsteps, video=withplots
                          == 'video') if withplots else lambda *args: None

    # start time stepping
    for istep in log.range('timestep', nsteps):

        Emix = F(c)
        Eiface = .5 * (c.grad(geom)**2).sum(-1)
        Ewall = (abs(ewall) + ewall * c)

        b = cbasis * c - mubasis * f_n(c)
        rhs, total, energy_mix, energy_iface = domain.integrate(
            [b, c, Emix, Eiface], geometry=geom, ischeme='gauss4')
        energy_wall = domain.boundary.integrate(Ewall,
                                                geometry=geom,
                                                ischeme='gauss4')
        log.user('concentration {}, energy {}'.format(
            total, energy_mix + energy_iface + energy_wall))

        lhs = matrix.solve(rhs0 + rhs, tol=1e-12, restart=999)
        c = cbasis.dot(lhs)
        mu = mubasis.dot(lhs)

        makeplots(c, mu, energy_mix, energy_iface, energy_wall)

    return lhs, energy_mix, energy_iface, energy_wall
Пример #27
0
    def _tablulate_control_mapping(self):
        '''
            Tabulate the control mapping and all its required derivatives
            in the quadrature points.
            XXX: see if we can make this look prettier
                (possibly using some symbolic approach).
        '''

        # by the time this is called, _f and _quad have been set
        # in the __init__ method
        _f = self._f
        _q = self._quad

        # all required first and second derivatives
        X_xi = _f(*_q, dx=1)
        X_xi_eta = _f(*_q, dx=1, dy=1)
        X_xi_xi = _f(*_q, dx=2)
        X_eta = _f(*_q, dy=1)
        X_eta_eta = _f(*_q, dy=2)

        _0 = (Ellipsis, 0)
        _1 = (Ellipsis, 1)

        # Jacobian determinant and its derivatives
        jacdet = X_xi[_0] * X_eta[_1] - X_eta[_0] * X_xi[_1]
        jacsqrt = jacdet**2
        jacdet_xi = X_xi_xi[_0] * X_eta[_1] + X_xi[_0] * X_xi_eta[_1] \
            - X_xi_eta[_0] * X_xi[_1] - X_eta[_0] * X_xi_xi[_1]
        jacdet_eta = X_xi_eta[_0] * X_eta[_1] + X_xi[_0] * X_eta_eta[_1] \
            - X_eta_eta[_0] * X_xi[_1] - X_eta[_0] * X_xi_eta[_1]

        if not (jacdet > 0).all():
            log.warning('''
                    Warning, the control mapping is defective on at least one
                    quadrature point. Proceeding with this control mapping may
                    lead to failure of convergence and / or a defective mapping.
                ''')

        # metric tensor of the control mapping divided by the Jacobian determinant
        self._G11 = (X_xi**2).sum(-1) / jacdet
        self._G12 = (X_xi * X_eta).sum(-1) / jacdet
        self._G22 = (X_eta**2).sum(-1) / jacdet

        # derivatives of _G11
        self._G11_xi = \
            2 * ( X_xi * X_xi_xi ).sum(-1) / jacdet \
            - jacdet_xi * (X_xi ** 2).sum(-1) / jacsqrt
        self._G11_eta = \
            2 * ( X_xi * X_xi_eta ).sum(-1) / jacdet \
            - jacdet_eta * (X_xi ** 2).sum(-1) / jacsqrt

        # derivatives of _G12
        self._G12_xi = \
            (X_xi_xi * X_eta + X_xi * X_xi_eta).sum(-1) / jacdet  \
            - jacdet_xi * (X_xi * X_eta).sum(-1) / jacsqrt
        self._G12_eta = \
            (X_xi_eta * X_eta + X_xi * X_eta_eta).sum(-1) / jacdet \
            - jacdet_eta * (X_xi * X_eta).sum(-1) / jacsqrt

        # derivatives of _G22
        self._G22_xi = \
            2 * ( X_eta * X_xi_eta ).sum(-1) / jacdet \
            - jacdet_xi * (X_eta ** 2).sum(-1) / jacsqrt
        self._G22_eta = \
            2 * ( X_eta * X_eta_eta ).sum(-1) / jacdet \
            - jacdet_eta * (X_eta ** 2).sum(-1) / jacsqrt

        log.info('The control mapping has been tabulated.')
Пример #28
0
def main(
    nelems: 'number of elements' = 12,
    viscosity: 'fluid viscosity' = 1e-2,
    density: 'fluid density' = 1,
    tol: 'solver tolerance' = 1e-12,
    rotation: 'cylinder rotation speed' = 0,
    timestep: 'time step' = 1/24,
    maxradius: 'approximate domain size' = 25,
    tmax: 'end time' = numpy.inf,
    degree: 'polynomial degree' = 2,
    figures: 'create figures' = True,
  ):

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

  # create namespace
  ns = function.Namespace()
  ns.uinf = 1, 0
  ns.density = density
  ns.viscosity = viscosity

  # construct mesh
  rscale = numpy.pi / nelems
  melems = numpy.ceil(numpy.log(2*maxradius) / rscale).astype(int)
  log.info('creating {}x{} mesh, outer radius {:.2f}'.format(melems, 2*nelems, .5*numpy.exp(rscale*melems)))
  domain, x0 = mesh.rectilinear([range(melems+1),numpy.linspace(0,2*numpy.pi,2*nelems+1)], periodic=(1,))
  rho, phi = x0
  phi += 1e-3 # tiny nudge (0.057 deg) to break element symmetry
  radius = .5 * function.exp(rscale * rho)
  ns.x = radius * function.trigtangent(phi)
  domain = domain.withboundary(inner='left', inflow=domain.boundary['right'].select(-ns.uinf.dotnorm(ns.x), ischeme='gauss1'))

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

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

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

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

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

  return lhs0, lhs
Пример #29
0
def main(
    nelems: 'number of elements' = 12,
    viscosity: 'fluid viscosity' = 1e-2,
    density: 'fluid density' = 1,
    tol: 'solver tolerance' = 1e-12,
    rotation: 'cylinder rotation speed' = 0,
    timestep: 'time step' = 1/24,
    maxradius: 'approximate domain size' = 25,
    tmax: 'end time' = numpy.inf,
    withplots: 'create plots' = True,
  ):

  uinf = numpy.array([ 1, 0 ])
  log.user( 'reynolds number:', density/viscosity )

  # construct mesh
  rscale = numpy.pi / nelems
  melems = numpy.ceil( numpy.log(2*maxradius) / rscale ).astype( int )
  log.info( 'creating {}x{} mesh, outer radius {:.2f}'.format( melems, 2*nelems, .5*numpy.exp(rscale*melems) ) )
  domain, gridgeom = mesh.rectilinear( [range(melems+1),numpy.linspace(0,2*numpy.pi,2*nelems+1)], periodic=(1,) )
  rho, phi = gridgeom
  phi += 1e-3 # tiny nudge (0.057 deg) to break element symmetry
  cylvelo = -.5 * rotation * function.trignormal(phi)
  radius = .5 * function.exp( rscale * rho )
  geom = radius * function.trigtangent(phi)

  # prepare bases
  J = geom.grad( gridgeom )
  detJ = function.determinant( J )
  vnbasis, vtbasis, pbasis = function.chain([ # compatible spaces using piola transformation
    domain.basis( 'spline', degree=(3,2), removedofs=((0,),None) )[:,_] * J[:,0] / detJ,
    domain.basis( 'spline', degree=(2,3) )[:,_] * J[:,1] / detJ,
    domain.basis( 'spline', degree=2 ) / detJ,
  ])
  vbasis = vnbasis + vtbasis
  stressbasis = (2*viscosity) * vbasis.symgrad(geom) - pbasis[:,_,_] * function.eye( domain.ndims )

  # prepare matrices
  A = function.outer( vbasis.grad(geom), stressbasis ).sum([2,3]) + function.outer( pbasis, vbasis.div(geom) )
  Ao = function.outer( vbasis, density * ( vbasis.grad(geom) * uinf ).sum(-1) ).sum(-1)
  At = (density/timestep) * function.outer( vbasis ).sum(-1)
  Ad = function.outer( vbasis.div(geom) )
  stokesmat, uniconvec, inertmat, divmat = domain.integrate( [ A, Ao, At, Ad ], geometry=geom, ischeme='gauss9' )

  # interior boundary condition (weak imposition of shear verlocity component)
  inner = domain.boundary['left']
  h = inner.elem_eval( 1, geometry=geom, ischeme='gauss9', asfunction=True )
  nietzsche = (2*viscosity) * ( (7.5/h) * vbasis - vbasis.symgrad(geom).dotnorm(geom) )
  B = function.outer( nietzsche, vbasis ).sum(-1)
  b = ( nietzsche * cylvelo ).sum(-1)
  bcondmat, rhs = inner.integrate( [ B, b ], geometry=geom, ischeme='gauss9' )
  stokesmat += bcondmat

  # exterior boundary condition (full velocity vector imposed at inflow)
  inflow = domain.boundary['right'].select( -( uinf * geom.normal() ).sum(-1), ischeme='gauss1' )
  cons = inflow.project( uinf, onto=vbasis, geometry=geom, ischeme='gauss9', tol=1e-12 )

  # initial condition (stationary oseen flow)
  lhs = (stokesmat+uniconvec).solve( rhs, constrain=cons, tol=0 )

  # prepare plotting
  if not withplots:
    makeplots = lambda *args: None
  else:
    makeplots = MakePlots( domain, geom, video=withplots=='video', timestep=timestep )
    mesh_ = domain.elem_eval( geom, ischeme='bezier5', separate=True )
    inflow_ = inflow.elem_eval( geom, ischeme='bezier5', separate=True )
    with plot.PyPlot( 'mesh', ndigits=0 ) as plt:
      plt.mesh( mesh_ )
      plt.rectangle( makeplots.bbox[:,0], *( makeplots.bbox[:,1] - makeplots.bbox[:,0] ), ec='green' )
      plt.segments( inflow_, colors='red' )

  # start time stepping
  stokesmat += inertmat
  precon = (stokesmat+uniconvec).getprecon( 'splu', constrain=cons )
  for istep in log.count( 'timestep', start=1 ):
    log.info( 'velocity divergence:', divmat.matvec(lhs).dot(lhs) )
    convection = density * ( vbasis.grad(geom) * vbasis.dot(lhs) ).sum(-1)
    matrix = stokesmat + domain.integrate( function.outer( vbasis, convection ).sum(-1), ischeme='gauss9', geometry=geom )
    lhs = matrix.solve( rhs + inertmat.matvec(lhs), lhs0=lhs, constrain=cons, tol=tol, restart=9999, precon=precon )
    makeplots( vbasis.dot(lhs), pbasis.dot(lhs), istep*timestep*rotation )
    if istep*timestep > tmax:
      break

  return lhs
Пример #30
0
def runtests():

  args = sys.argv[1:] # command line arguments

  if 'coverage' in sys.argv[0]:
    # coverage passes the complete commandline to `sys.argv`
    # find '-m tests' and keep the tail
    m = args.index( '-m' )
    assert args[m+1] == __name__
    args = args[m+2:]

  __tbexplore__ = '--tbexplore' in args
  if __tbexplore__:
    args.remove( '--tbexplore' )
  
  if args:
    assert len(args) == 1
    whitelist = args[0].split( '.' )
  else:
    whitelist = []

  __richoutput__ = True
  __selfcheck__ = True
  __log__ = log._mklog()
  try:
    results = _runtests( PACKAGES, whitelist )
  except KeyboardInterrupt:
    log.info( 'aborted.' )
    sys.exit( -1 )
  except:
    exc, frames = debug.exc_info()
    log.stack( 'error in unit testing framework: {}'.format(exc), frames )
    log.info( 'crashed.' )
    sys.exit( -2 )

  summary = _summarize(results)
  ntests = sum( len(tests) for tests in summary.values() )
  passed = summary.pop( OK, [] )
  failed = summary.pop( FAILED, [] )
  error = summary.pop( ERROR, [] )
  pkgerror = summary.pop( PKGERROR, [] )

  log.info( '{}/{} tests passed.'.format( len(passed), ntests ) )
  if failed:
    log.info( '* failures ({}):'.format(len(failed)), ', '.join( failed ) )
  if error:
    log.info( '* errors ({}):'.format(len(error)), ', '.join( error ) )
  if pkgerror:
    log.info( '* package failures ({}):'.format(len(pkgerror)), ', '.join( pkgerror ) )
  if summary:
    log.info( '* invalid status ({}) - this should not happen!'.format(len(summary)) )

  sys.exit( ntests - len(passed) )
Пример #31
0
def main(
    uinf      = 2.0,
    L         = 2.0,
    R         = 0.5,
    nelems    = 7  ,
    degree    = 2  ,
    maxrefine = 3  ,
    withplots = True
    ):
  
  """`main` functions with different parameters.
  
  Parameters
  ----------
  uinf : float 
      free stream velocity
  L : float
      domain size
  R : float
      cylinder radius
  nelems : int
      number of elements
  degree : int
      b-spline degree
  maxrefine : int
      bisectioning steps
  withplots : bool
      create plots
  Returns
  -------
  lhs : float
      solution φ
  err : float
      L2 norm, H1 norm and energy errors
  """
  # construct mesh
  verts = numpy.linspace(-L/2, L/2, nelems+1)
  domain, geom = mesh.rectilinear([verts, verts])
  # trim out a circle
  domain = domain.trim(function.norm2(geom)-R, maxrefine=maxrefine)
  # initialize namespace
  ns = function.Namespace()
  ns.R    = R
  ns.x    = geom
  ns.uinf = uinf
  # construct function space and lagrange multiplier
  ns.phibasis, ns.lbasis = function.chain([domain.basis('spline', degree=degree),[1.]])
  ns.phi = ' phibasis_n ?lhs_n'
  ns.u   = 'sqrt( (phibasis_n,i ?lhs_n) (phibasis_m,i ?lhs_m) )'
  ns.l   = 'lbasis_n ?lhs_n'
  # set the exact solution
  ns.phiexact = 'uinf x_1 ( 1 - R^2 / (x_0^2 + x_1^2) )' # average is zero
  ns.phierror = 'phi - phiexact'
  # construct residual
  res  = domain.integral('-phibasis_n,i phi_,i' @ ns, geometry=ns.x, degree=degree*2)
  res += domain.boundary.integral('phibasis_n phiexact_,i n_i' @ ns, geometry=ns.x, degree=degree*2)
  res += domain.integral('lbasis_n phi + l phibasis_n' @ ns, geometry=ns.x, degree=degree*2)
  # find lhs such that res == 0 and substitute this lhs in the namespace
  lhs = solver.solve_linear('lhs', res)
  ns = ns(lhs=lhs)
  # evaluate error
  err1 = numpy.sqrt(domain.integrate(['phierror phierror' @ns,'phierror phierror + phierror_,i phierror_,i' @ns, '0.5 phi_,i phi_,i' @ns], geometry=ns.x, degree=degree*4))
  err2 = abs(err1[2] - 2.7710377946088443)
  err = numpy.array([err1[0],err1[1],err2])
  log.info('errors: L2={:.2e}, H1={:.2e}, eh={:.2e}'.format(*err))
  if withplots:
    makeplots(domain, ns)
  return lhs, err