def test_bases(mu, case): domain, vbasis, pbasis = case.domain, case.basis('v'), case.basis('p') trfgeom = case.geometry(mu) refgeom = case.refgeom a_vbasis, a_pbasis = piola_bases(mu, case) J = trfgeom.grad(case.geometry()) detJ = fn.determinant(J) b_vbasis = fn.matmat(vbasis, J.transpose()) / detJ b_pbasis = pbasis / detJ Z = case.geometry(mu).grad(case.geometry()) detZ = fn.determinant(Z) zdiff = np.sqrt(domain.integrate((Z - J)**2 * fn.J(refgeom), ischeme='gauss9').export('dense')) np.testing.assert_almost_equal(zdiff, 0.0) c_vbasis = fn.matmat(vbasis, Z.transpose()) / detZ c_pbasis = pbasis pdiff = np.sqrt(domain.integrate((a_pbasis - b_pbasis)**2 * fn.J(refgeom), ischeme='gauss9')) np.testing.assert_almost_equal(pdiff, 0.0) pdiff = domain.integrate((a_pbasis - c_pbasis)**2 * fn.J(refgeom), ischeme='gauss9') np.testing.assert_almost_equal(pdiff, 0.0) vdiff = np.sqrt(domain.integrate((a_vbasis - b_vbasis)**2 * fn.J(refgeom), ischeme='gauss9').export('dense')) np.testing.assert_almost_equal(vdiff, 0.0) vdiff = domain.integrate((a_vbasis - c_vbasis)**2 * fn.J(refgeom), ischeme='gauss9').export('dense') np.testing.assert_almost_equal(vdiff, 0.0)
def splipy_to_nutils(spline): """ Returns nutils domain and geometry object for spline mapping given by the argument """ from nutils import mesh, function domain, geom = mesh.rectilinear(spline.knots()) cp = controlpoints(spline) basis = domain.basis('spline', degree=degree(spline), knotmultiplicities=multiplicities(spline)) geom = function.matmat(basis, cp) #TODO: add correct behaviour for rational and/or periodic geometries return domain, geom
def basis(self, name, mu=None, transform=True): func = super().basis(name, mu=mu) if transform and f'{name}-trf' in self and mu is not None: J = self[f'{name}-trf'](mu) if J.ndim == 0: func = func * J else: func = fn.matmat(func, J.transpose()) return func
def solution(self, lhs, field, mu=None, lift=True, J=None): lhs = self.solution_vector(lhs, mu, lift) if J is None: func = self.basis(field, mu).dot(lhs) else: func = self.basis(field, mu, transform=False) if J.ndim == 0: func = func * J else: func = fn.matmat(func, J.transpose()) func = func.dot(lhs) return self.domain.sample(*element.parse_legacy_ischeme(self.meta['vscheme'])).eval(func)
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)
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' J = ns.x.grad(geom) detJ = function.determinant(J) ns.ubasis = function.matmat(function.vectorize([ domain.basis('spline', degree=(degree,degree-1), removedofs=((0,),None)), domain.basis('spline', degree=(degree-1,degree))]), J.T) / detJ ns.pbasis = domain.basis('spline', degree=degree-1) / detJ ns.u_i = 'ubasis_ni ?u_n' ns.p = 'pbasis_n ?p_n' ns.sigma_ij = '(d(u_i, x_j) + d(u_j, x_i)) / Re - p δ_ij' ns.N = 10 * degree / elemangle # Nitsche constant based on element size = elemangle/2 ns.nitsche_ni = '(N ubasis_ni - (d(ubasis_ni, x_j) + d(ubasis_nj, x_i)) n_j) / Re' 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('sum((u - uinf)^2)' @ ns, degree=degree*2) ucons = solver.optimize('u', sqr, droptol=1e-15) # constrain inflow semicircle to uinf cons = dict(u=ucons) numpy.random.seed(seed) sqr = domain.integral('sum((u - uinf)^2)' @ ns, degree=degree*2) udofs0 = solver.optimize('u', sqr) * numpy.random.normal(1, .1, len(ns.ubasis)) # set initial condition to u=uinf with small random noise state0 = dict(u=udofs0) ures = domain.integral('(ubasis_ni d(u_i, x_j) u_j + d(ubasis_ni, x_j) sigma_ij) J(x)' @ ns, degree=9) ures += domain.boundary['inner'].integral('(nitsche_ni (u_i - uwall_i) - ubasis_ni sigma_ij n_j) J(x)' @ ns, degree=9) pres = domain.integral('pbasis_n d(u_k, x_k) J(x)' @ ns, degree=9) uinertia = domain.integral('ubasis_ni u_i J(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(('u', 'p'), residual=(ures, pres), inertia=(uinertia, None), arguments=state0, timestep=timestep, constrain=cons, newtontol=1e-10)) as steps: for istep, state in enumerate(steps): t = istep * timestep x, u, normu, p = bezier.eval(['x', 'u', 'norm2(u)', 'p'] @ ns, **state) 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 state0, state
def __init__(self, nel=10, L=15, penalty=1e10, ratio=1000, override=False, finalize=True): L /= 5 hnel = int(L * 5 * nel // 2) xpts = np.linspace(0, L, 2 * hnel + 1) yzpts = np.linspace(0, 0.2, nel + 1) domain, geom = mesh.rectilinear([xpts, yzpts]) dom1 = domain[:hnel, :] dom2 = domain[hnel:, :] NutilsCase.__init__(self, 'Elastic split beam', domain, geom) E1 = self.parameters.add('ymod1', 1e10, 9e10) E2 = self.parameters.add('ymod2', 1e10 / ratio, 9e10 / ratio) NU = self.parameters.add('prat', 0.25, 0.42) F1 = self.parameters.add('force1', -1e8, 1e8) mults = [[2] + [1] * (hnel - 1) + [2] + [1] * (hnel - 1) + [2], [2] + [1] * (nel - 1) + [2]] basis = domain.basis('spline', degree=1, knotmultiplicities=mults).vector(2) blen = len(basis) basis, *__ = fn.chain([basis, [0] * (2 * (nel + 1))]) self.bases.add('u', basis, length=blen) self.extra_dofs = (nel + 1) * 2 self.lift += 1, np.zeros((len(basis), )) self.constrain('u', 'left') self.constrain('u', 'right') MU1 = E1 / (1 + NU) MU2 = E2 / (1 + NU) LAMBDA1 = E1 * NU / (1 + NU) / (1 - 2 * NU) LAMBDA2 = E2 * NU / (1 + NU) / (1 - 2 * NU) itg_mu = fn.outer(basis.symgrad(geom)).sum([-1, -2]) itg_la = fn.outer(basis.div(geom)) self['stiffness'] += MU1, NutilsArrayIntegrand(itg_mu).prop( domain=dom1) self['stiffness'] += MU2, NutilsArrayIntegrand(itg_mu).prop( domain=dom2) self['stiffness'] += LAMBDA1, NutilsArrayIntegrand(itg_la).prop( domain=dom1) self['stiffness'] += LAMBDA2, NutilsArrayIntegrand(itg_la).prop( domain=dom2) # Penalty term ... quite hacky K = blen // 2 ldofs = list(range(hnel * (nel + 1), (hnel + 1) * (nel + 1))) rdofs = list(range((hnel + 1) * (nel + 1), (hnel + 2) * (nel + 1))) ldofs += [k + K for k in ldofs] rdofs += [k + K for k in rdofs] L = len(ldofs) Linds = list(range(self.ndofs - L, self.ndofs)) pen = sp.coo_matrix(((np.ones(L), (Linds, ldofs))), shape=(self.ndofs, self.ndofs)) pen += sp.coo_matrix(((-np.ones(L), (Linds, rdofs))), shape=(self.ndofs, self.ndofs)) self['penalty'] += 1, ScipyArrayIntegrand(sp.csc_matrix(pen + pen.T)) # Even more hacky for the ROM part bnd_lft = domain[:hnel, :].boundary['right'] bnd_rgt = domain[hnel:, :].boundary['left'] pts, wts = bnd_lft.elements[0].reference.getischeme('gauss5') penalty_mx = np.zeros((self.ndofs, self.ndofs)) for el_lft, el_rgt in zip(bnd_lft.elements, bnd_rgt.elements): udiff = ( basis.eval(_points=pts, _transforms=(el_lft.transform, el_lft.opposite)) - basis.eval(_points=pts, _transforms=(el_rgt.transform, el_rgt.opposite))) penalty_mx += (udiff[:, :, _, :] * udiff[:, _, :, :] * wts[:, _, _, _]).sum((0, -1)) self['penalty-rom'] += 1e20, sp.csc_matrix(penalty_mx) normdot = fn.matmat(basis, geom.normal()) force_bnd = domain[hnel - 11:hnel - 1, :].boundary['top'] self['forcing'] += F1, NutilsArrayIntegrand(normdot).prop( domain=force_bnd) self['u-h1s'] = self['stiffness'] self['u-h1s-l'] = self['stiffness'] self['u-h1s-r'] = self['stiffness'] self['u-h1s-e'] = self['stiffness'] self.verify()
import click import numpy as np from nutils import log, config, function as fn from aroma import cases, solvers, util, quadrature, reduction, ensemble as ens, visualization from aroma.affine import NumpyArrayIntegrand, integrate import multiprocessing import matplotlib.pyplot as plt def get_exforce(case, mu): v, p = case.exact(mu, ('v', 'p')) geom = case.geometry(mu) force = p * geom.normal() - fn.matmat(v.grad(geom), geom.normal()) / mu['re'] return case.domain.boundary['bottom'].integrate(force, ischeme='gauss9', geometry=geom) def get_locforce(case, mu, lhs): return case['force'](mu, cont=(lhs, None)) def get_globforce(case, mu, lhs): wx, wy, l = case['xforce'](mu), case['yforce'](mu), case.lift(mu) u = lhs + l getf = lambda w: (case['divergence'](mu, cont=(w, u)) + case['laplacian']( mu, cont=(w, u)) + integrate(case['convection'] (mu, cont=(w, u, u))) - case['forcing'] (mu, cont=(w, )))
def __init__(self, nelems=10, piola=False, degree=2): pts = np.linspace(0, 1, nelems + 1) domain, geom = mesh.rectilinear([pts, pts]) NutilsCase.__init__(self, 'Abdulahque Manufactured Solution', domain, geom) RE = 1 / self.parameters.add('re', 100.0, 150.0) T = self.parameters.add('time', 0.0, 10.0) # Add bases and construct a lift function vbasis, pbasis = mk_bases(self, piola, degree) self.constrain('v', 'bottom') # self.constrain('v', 'top') # self.constrain('v', 'left') # self.constrain('v', 'right') self.lift += 1, np.zeros(len(vbasis)) self['divergence'] -= 1, fn.outer(vbasis.div(geom), pbasis) self['divergence'].freeze(lift=(1, )) self['laplacian'] += RE, fn.outer(vbasis.grad(geom)).sum((-1, -2)) self['convection'] += 1, NutilsDelayedIntegrand('w_ia u_jb v_ka,b', 'ijk', 'wuv', x=geom, w=vbasis, u=vbasis, v=vbasis) self['v-h1s'] += 1, fn.outer(vbasis.grad(geom)).sum((-1, -2)) self['v-l2'] += 1, fn.outer(vbasis).sum((-1, )) self['p-l2'] += 1, fn.outer(pbasis) # Body force x, y = geom h = T h1 = 1 f = 4 * (x - x**2)**2 f1 = f.grad(geom)[0] f2 = f1.grad(geom)[0] f3 = f2.grad(geom)[0] g = 4 * (y - y**2)**2 g1 = g.grad(geom)[1] g2 = g1.grad(geom)[1] g3 = g2.grad(geom)[1] # Body force self['forcing'] += h**2, fn.matmat( vbasis, fn.asarray([ (g1**2 - g * g2) * f * f1, (f1**2 - f * f2) * g * g1, ])) self['forcing'] += RE * h, fn.matmat( vbasis, fn.asarray([-f * g3, f3 * g + 2 * f1 * g2])) self['forcing'] += h1, fn.matmat(vbasis, fn.asarray([f * g1, -f1 * g])) # Neumann conditions mx = fn.asarray([[f1 * g1, f * g2], [-f2 * g, -f1 * g1]]) hh = fn.matmat(mx, geom.normal()) - f1 * g1 * geom.normal() self['forcing'] += RE * h, NutilsArrayIntegrand(fn.matmat( vbasis, hh)).prop(domain=domain.boundary['left'] | domain.boundary['right'] | domain.boundary['top'])
def exact(self, mu, field): scale = mu['w']**(self.power-1) * mu['h']**(self.power-1) retval = scale * self._exact_solutions[field] if field == 'v': return fn.matmat(fn.asarray([[mu['w'], 0], [0, mu['h']]]), retval) return retval
def __init__(self, nel=10, ndim=2, L=15, override=False, finalize=True, graded=False): L /= 5 if graded: # HACK! xpts = _graded(0, L, int(L * 3 * nel + 1), 1.04) yzpts = _graded(0, 0.1, nel // 2 + 1, 1.3) yzpts = np.hstack([yzpts[:-1], 0.2 - yzpts[::-1]]) else: xpts = np.linspace(0, L, int(L * 5 * nel + 1)) yzpts = np.linspace(0, 0.2, nel + 1) if ndim == 2: domain, geom = mesh.rectilinear([xpts, yzpts]) else: domain, geom = mesh.rectilinear([xpts, yzpts, yzpts]) NutilsCase.__init__(self, 'Elastic beam', domain, geom) E = self.parameters.add('ymod', 1e10, 9e10) NU = self.parameters.add('prat', 0.25, 0.42) F1 = self.parameters.add('force1', -0.4e6, 0.4e6) F2 = self.parameters.add('force2', -0.2e6, 0.2e6) if ndim == 3: F3 = self.parameters.add('force3', -0.2e6, 0.2e6) basis = domain.basis('spline', degree=1).vector(ndim) self.bases.add('u', basis, length=len(basis)) self.lift += 1, np.zeros((len(basis), )) self.constrain('u', 'left') MU = E / (1 + NU) LAMBDA = E * NU / (1 + NU) / (1 - 2 * NU) self['stiffness'] += MU, fn.outer(basis.symgrad(geom)).sum([-1, -2]) self['stiffness'] += LAMBDA, fn.outer(basis.div(geom)) normdot = fn.matmat(basis, geom.normal()) irgt = NutilsArrayIntegrand(normdot).prop( domain=domain.boundary['right']) ibtm = NutilsArrayIntegrand(normdot).prop( domain=domain.boundary['bottom']) itop = NutilsArrayIntegrand(normdot).prop( domain=domain.boundary['top']) self['forcing'] += F1, irgt self['forcing'] -= F2, ibtm self['forcing'] += F2, itop if ndim == 3: ifrt = NutilsArrayIntegrand(normdot).prop( domain=domain.boundary['front']) ibck = NutilsArrayIntegrand(normdot).prop( domain=domain.boundary['back']) self['forcing'] -= F3, ifrt self['forcing'] += F3, ibck self['u-h1s'] += 1, fn.outer(basis.grad(geom)).sum([-1, -2]) self.verify()