def __call__(self, lhs): angle = self.index * self.timestep * self.rotation ns = self.ns(lhs=lhs) x, u, normu, p = self.plotdomain.elem_eval( [ns.x, ns.u, function.norm2(ns.u), ns.p], ischeme='bezier9', separate=True) with plot.PyPlot('flow', index=self.index) as plt: plt.axes([0, 0, 1, 1], yticks=[], xticks=[], frame_on=False) tri = plt.mesh(x, normu, mergetol=1e-5, cmap='jet') plt.clim(0, 1.5) plt.tricontour(tri, p, every=self.every, cmap='gray', linestyles='solid', alpha=.8) uv = plot.interpolate(tri, self.xy, u) plt.vectors(self.xy, uv, zorder=9, pivot='mid', stems=False) plt.plot(0, 0, 'k', marker=(3, 2, angle * 180 / numpy.pi - 90), markersize=20) plt.xlim(self.bbox[0]) plt.ylim(self.bbox[1]) self.xy = util.regularize(self.bbox, self.spacing, self.xy + uv * self.timestep) self.index += 1
def main(viscosity=1.3e-3, density=1e3, pout=223e5, uw=-0.01, nelems=10): # viscosity = 1.3e-3 # density = 1e3 # pout = 223e5 # nelems = 10 # uw = -0.01 domain, geom = mesh.rectilinear([numpy.linspace(0, 1, nelems), numpy.linspace(1, 2, nelems), [0, 2 * numpy.pi]], periodic=[2]) domain = domain.withboundary(inner='bottom', outer='top') ns = function.Namespace() ns.y, ns.r, ns.θ = geom ns.x_i = '<r cos(θ), y, r sin(θ)>_i' ns.uybasis, ns.urbasis, ns.pbasis = function.chain([ domain.basis('std', degree=3, removedofs=((0,-1), None, None)), # remove normal component at y=0 and y=1 domain.basis('std', degree=3, removedofs=((0,-1), None, None)), # remove tangential component at y=0 (no slip) domain.basis('std', degree=2)]) ns.ubasis_ni = '<urbasis_n cos(θ), uybasis_n, urbasis_n sin(θ)>_i' ns.viscosity = viscosity ns.density = density ns.u_i = 'ubasis_ni ?lhs_n' ns.p = 'pbasis_n ?lhs_n' ns.sigma_ij = 'viscosity (u_i,j + u_j,i) - p δ_ij' ns.pout = pout ns.uw = uw ns.uw_i = 'uw <cos(phi), 0, sin(phi)>_i' ns.tout_i = '-pout n_i' ns.uw_i = 'uw n_i' # uniform outflow res = domain.integral('(viscosity ubasis_ni,j u_i,j - p ubasis_ni,i + pbasis_n u_k,k) d:x' @ ns, degree=6) # res -= domain[1].boundary['inner'].integral('ubasis_ni tout_i d:x' @ ns, degree=6) sqr = domain.boundary['inner'].integral('(u_i - uw_i) (u_i - uw_i) d:x' @ ns, degree=6) # sqr = domain.boundary['outer'].integral('(u_i - uin_i) (u_i - uin_i) d:x' @ ns, degree=6) sqr -= domain.boundary['outer'].integral('(p - pout) (p - pout) d:x' @ ns, degree=6) cons = solver.optimize('lhs', sqr, droptol=1e-15) lhs = solver.solve_linear('lhs', res, constrain=cons) plottopo = domain[:, :, 0:].boundary['back'] bezier = plottopo.sample('bezier', 10) r, y, p, u = bezier.eval([ns.r, ns.y, ns.p, function.norm2(ns.u)], lhs=lhs) with export.mplfigure('pressure.png', dpi=800) as fig: ax = fig.add_subplot(111, title='pressure', aspect=1) ax.autoscale(enable=True, axis='both', tight=True) im = ax.tripcolor(r, y, bezier.tri, p, shading='gouraud', cmap='jet') ax.add_collection( collections.LineCollection(numpy.array([y, r]).T[bezier.hull], colors='k', linewidths=0.2, alpha=0.2)) fig.colorbar(im) uniform = plottopo.sample('uniform', 1) r_, y_, uv = uniform.eval([ns.r, ns.y, ns.u], lhs=lhs) with export.mplfigure('velocity.png', dpi=800) as fig: ax = fig.add_subplot(111, title='Velocity', aspect=1) ax.autoscale(enable=True, axis='both', tight=True) im = ax.tripcolor(r, y, bezier.tri, u, shading='gouraud', cmap='jet') ax.quiver(r_, y_, uv[:, 0], uv[:, 1], angles='xy', scale_units='xy') fig.colorbar(im)
def main(nelems:int, etype:str, btype:str, degree:int, poisson:float, angle:float, restol:float, trim:bool): ''' Deformed hyperelastic plate. .. arguments:: nelems [10] Number of elements along edge. etype [square] Type of elements (square/triangle/mixed). btype [std] Type of basis function (std/spline). degree [1] Polynomial degree. poisson [.25] Poisson's ratio, nonnegative and stricly smaller than 1/2. angle [20] Rotation angle for right clamp (degrees). restol [1e-10] Newton tolerance. trim [no] Create circular-shaped hole. ''' domain, geom = mesh.unitsquare(nelems, etype) if trim: domain = domain.trim(function.norm2(geom-.5)-.2, maxrefine=2) bezier = domain.sample('bezier', 5) ns = function.Namespace(fallback_length=domain.ndims) ns.x = geom ns.angle = angle * numpy.pi / 180 ns.lmbda = 2 * poisson ns.mu = 1 - 2 * poisson ns.ubasis = domain.basis(btype, degree=degree) ns.u_i = 'ubasis_k ?lhs_ki' ns.X_i = 'x_i + u_i' ns.strain_ij = '.5 (d(u_i, x_j) + d(u_j, x_i))' ns.energy = 'lmbda strain_ii strain_jj + 2 mu strain_ij strain_ij' sqr = domain.boundary['left'].integral('u_k u_k J(x)' @ ns, degree=degree*2) sqr += domain.boundary['right'].integral('((u_0 - x_1 sin(2 angle) - cos(angle) + 1)^2 + (u_1 - x_1 (cos(2 angle) - 1) + sin(angle))^2) J(x)' @ ns, degree=degree*2) cons = solver.optimize('lhs', sqr, droptol=1e-15) energy = domain.integral('energy J(x)' @ ns, degree=degree*2) lhs0 = solver.optimize('lhs', energy, constrain=cons) X, energy = bezier.eval(['X', 'energy'] @ ns, lhs=lhs0) export.triplot('linear.png', X, energy, tri=bezier.tri, hull=bezier.hull) ns.strain_ij = '.5 (d(u_i, x_j) + d(u_j, x_i) + d(u_k, x_i) d(u_k, x_j))' ns.energy = 'lmbda strain_ii strain_jj + 2 mu strain_ij strain_ij' energy = domain.integral('energy J(x)' @ ns, degree=degree*2) lhs1 = solver.minimize('lhs', energy, arguments=dict(lhs=lhs0), constrain=cons).solve(restol) X, energy = bezier.eval(['X', 'energy'] @ ns, lhs=lhs1) export.triplot('nonlinear.png', X, energy, tri=bezier.tri, hull=bezier.hull) return lhs0, lhs1
def __call__( self, velo, pres, angle ): self.index += 1 points, velo, flow, pres = self.plotdomain.elem_eval( [ self.geom, velo, function.norm2(velo), pres ], ischeme='bezier9', separate=True ) with self.plt if self.plt else plot.PyPlot( 'flow', index=self.index ) as plt: plt.axes( [0,0,1,1], yticks=[], xticks=[], frame_on=False ) tri = plt.mesh( points, flow, mergetol=1e-5 ) plt.clim( 0, 1.5 ) plt.tricontour( tri, pres, every=self.every, cmap='gray', linestyles='solid', alpha=.8 ) uv = plot.interpolate( tri, self.xy, velo ) plt.vectors( self.xy, uv, zorder=9, pivot='mid', stems=False ) plt.plot( 0, 0, 'k', marker=(3,2,angle*180/numpy.pi-90), markersize=20 ) plt.xlim( self.bbox[0] ) plt.ylim( self.bbox[1] ) self.xy = util.regularize( self.bbox, self.spacing, self.xy + uv * self.timestep )
def __call__(self, lhs): angle = self.index * self.timestep * self.rotation ns = self.ns(lhs=lhs) x, u, normu, p = self.plotdomain.elem_eval([ns.x, ns.u, function.norm2(ns.u), ns.p], ischeme='bezier9', separate=True) with plot.PyPlot('flow', index=self.index) as plt: plt.axes([0,0,1,1], yticks=[], xticks=[], frame_on=False) tri = plt.mesh(x, normu, mergetol=1e-5, cmap='jet') plt.clim(0, 1.5) plt.tricontour(tri, p, every=self.every, cmap='gray', linestyles='solid', alpha=.8) uv = plot.interpolate(tri, self.xy, u) plt.vectors(self.xy, uv, zorder=9, pivot='mid', stems=False) plt.plot(0, 0, 'k', marker=(3,2,angle*180/numpy.pi-90), markersize=20) plt.xlim(self.bbox[0]) plt.ylim(self.bbox[1]) self.xy = util.regularize(self.bbox, self.spacing, self.xy + uv * self.timestep) self.index += 1
def main(nelems: int, etype: str, btype: str, degree: int, traction: float, maxrefine: int, radius: float, poisson: float): ''' Horizontally loaded linear elastic plate with FCM hole. .. arguments:: nelems [9] Number of elements along edge. etype [square] Type of elements (square/triangle/mixed). btype [std] Type of basis function (std/spline), with availability depending on the selected element type. degree [2] Polynomial degree. traction [.1] Far field traction (relative to Young's modulus). maxrefine [2] Number or refinement levels used for the finite cell method. radius [.5] Cut-out radius. poisson [.3] Poisson's ratio, nonnegative and strictly smaller than 1/2. ''' domain0, geom = mesh.unitsquare(nelems, etype) domain = domain0.trim(function.norm2(geom) - radius, maxrefine=maxrefine) ns = function.Namespace() ns.x = geom ns.lmbda = 2 * poisson ns.mu = 1 - poisson ns.ubasis = domain.basis(btype, degree=degree).vector(2) ns.u_i = 'ubasis_ni ?lhs_n' ns.X_i = 'x_i + u_i' ns.strain_ij = '(d(u_i, x_j) + d(u_j, x_i)) / 2' ns.stress_ij = 'lmbda strain_kk δ_ij + 2 mu strain_ij' ns.r2 = 'x_k x_k' ns.R2 = radius**2 / ns.r2 ns.k = (3 - poisson) / (1 + poisson) # plane stress parameter ns.scale = traction * (1 + poisson) / 2 ns.uexact_i = 'scale (x_i ((k + 1) (0.5 + R2) + (1 - R2) R2 (x_0^2 - 3 x_1^2) / r2) - 2 δ_i1 x_1 (1 + (k - 1 + R2) R2))' ns.du_i = 'u_i - uexact_i' sqr = domain.boundary['left,bottom'].integral('(u_i n_i)^2 J(x)' @ ns, degree=degree * 2) cons = solver.optimize('lhs', sqr, droptol=1e-15) sqr = domain.boundary['top,right'].integral('du_k du_k J(x)' @ ns, degree=20) cons = solver.optimize('lhs', sqr, droptol=1e-15, constrain=cons) res = domain.integral('d(ubasis_ni, x_j) stress_ij J(x)' @ ns, degree=degree * 2) lhs = solver.solve_linear('lhs', res, constrain=cons) bezier = domain.sample('bezier', 5) X, stressxx = bezier.eval(['X', 'stress_00'] @ ns, lhs=lhs) export.triplot('stressxx.png', X, stressxx, tri=bezier.tri, hull=bezier.hull) err = domain.integral('<du_k du_k, sum:ij(d(du_i, x_j)^2)>_n J(x)' @ ns, degree=max(degree, 3) * 2).eval(lhs=lhs)**.5 treelog.user('errors: L2={:.2e}, H1={:.2e}'.format(*err)) return err, cons, lhs
def mk_theta(geom, rmin, rmax): r = fn.norm2(geom) diam = rmax - rmin theta = (lambda x: (1 - x)**3 * (3 * x + 1))((r - rmin) / diam) theta = fn.piecewise(r, (rmin, rmax), 1, theta, 0) return theta
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
def main(nrefine: int, traction: float, radius: float, poisson: float): ''' Horizontally loaded linear elastic plate with IGA hole. .. arguments:: nrefine [2] Number of uniform refinements starting from 1x2 base mesh. traction [.1] Far field traction (relative to Young's modulus). radius [.5] Cut-out radius. poisson [.3] Poisson's ratio, nonnegative and strictly smaller than 1/2. ''' # create the coarsest level parameter domain domain, geom0 = mesh.rectilinear([1, 2]) bsplinebasis = domain.basis('spline', degree=2) controlweights = numpy.ones(12) controlweights[1:3] = .5 + .25 * numpy.sqrt(2) weightfunc = bsplinebasis.dot(controlweights) nurbsbasis = bsplinebasis * controlweights / weightfunc # create geometry function indices = [0, 2], [1, 2], [2, 1], [2, 0] controlpoints = numpy.concatenate([ numpy.take([0, 2**.5 - 1, 1], indices) * radius, numpy.take([0, .3, 1], indices) * (radius + 1) / 2, numpy.take([0, 1, 1], indices) ]) geom = (nurbsbasis[:, numpy.newaxis] * controlpoints).sum(0) radiuserr = domain.boundary['left'].integral( (function.norm2(geom) - radius)**2 * function.J(geom0), degree=9).eval()**.5 treelog.info('hole radius exact up to L2 error {:.2e}'.format(radiuserr)) # refine domain if nrefine: domain = domain.refine(nrefine) bsplinebasis = domain.basis('spline', degree=2) controlweights = domain.project(weightfunc, onto=bsplinebasis, geometry=geom0, ischeme='gauss9') nurbsbasis = bsplinebasis * controlweights / weightfunc ns = function.Namespace() ns.x = geom ns.lmbda = 2 * poisson ns.mu = 1 - poisson ns.ubasis = nurbsbasis.vector(2) ns.u_i = 'ubasis_ni ?lhs_n' ns.X_i = 'x_i + u_i' ns.strain_ij = '(d(u_i, x_j) + d(u_j, x_i)) / 2' ns.stress_ij = 'lmbda strain_kk δ_ij + 2 mu strain_ij' ns.r2 = 'x_k x_k' ns.R2 = radius**2 / ns.r2 ns.k = (3 - poisson) / (1 + poisson) # plane stress parameter ns.scale = traction * (1 + poisson) / 2 ns.uexact_i = 'scale (x_i ((k + 1) (0.5 + R2) + (1 - R2) R2 (x_0^2 - 3 x_1^2) / r2) - 2 δ_i1 x_1 (1 + (k - 1 + R2) R2))' ns.du_i = 'u_i - uexact_i' sqr = domain.boundary['top,bottom'].integral('(u_i n_i)^2 J(x)' @ ns, degree=9) cons = solver.optimize('lhs', sqr, droptol=1e-15) sqr = domain.boundary['right'].integral('du_k du_k J(x)' @ ns, degree=20) cons = solver.optimize('lhs', sqr, droptol=1e-15, constrain=cons) # construct residual res = domain.integral('d(ubasis_ni, x_j) stress_ij J(x)' @ ns, degree=9) # solve system lhs = solver.solve_linear('lhs', res, constrain=cons) # vizualize result bezier = domain.sample('bezier', 9) X, stressxx = bezier.eval(['X', 'stress_00'] @ ns, lhs=lhs) export.triplot('stressxx.png', X, stressxx, tri=bezier.tri, hull=bezier.hull, clim=(numpy.nanmin(stressxx), numpy.nanmax(stressxx))) # evaluate error err = domain.integral('<du_k du_k, sum:ij(d(du_i, x_j)^2)>_n J(x)' @ ns, degree=9).eval(lhs=lhs)**.5 treelog.user('errors: L2={:.2e}, H1={:.2e}'.format(*err)) return err, cons, lhs
import numpy as np from nutils import function as fn, matrix import pytest from aroma import cases from aroma.solvers import stokes, navierstokes def _check_exact(case, mu, lhs, with_p=True): vsol = case.basis('v', mu).dot(lhs + case.lift(mu)) psol = case.basis('p', mu).dot(lhs + case.lift(mu)) vexc, pexc = case.exact(mu, ['v', 'p']) vdiff = fn.norm2(vsol - vexc) pdiff = (psol - pexc)**2 geom = case.geometry(mu) verr, perr = case.domain.integrate( [vdiff * fn.J(geom), pdiff * fn.J(geom)], ischeme='gauss9') np.testing.assert_almost_equal(verr, 0.0) if with_p: np.testing.assert_almost_equal(perr, 0.0) @pytest.fixture() def cavity(): case = cases.cavity(nel=2) case.precompute() return case
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
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
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