def generate(self): treelog.user('my message') with treelog.infofile('test.dat', 'w') as f: f.write('test1') with treelog.context('my context'): with treelog.iter.plain('iter', 'abc') as items: for c in items: treelog.info(c) with treelog.context('empty'): pass treelog.error('multiple..\n ..lines') with treelog.userfile('test.dat', 'wb') as f: treelog.info('generating') f.write(b'test2') self.generate_test() with treelog.context('context step={}', 0) as format: treelog.info('foo') format(1) treelog.info('bar') with treelog.errorfile('same.dat', 'wb') as f: f.write(b'test3') with treelog.debugfile('dbg.dat', 'wb') as f: f.write(b'test4') treelog.debug('dbg') treelog.warning('warn')
def test_surface(self): trimsurface = self.pos.boundary['trimmed'].volume(self.geom) trimerr = abs(trimsurface - self.exact_trimsurface) / self.exact_trimsurface log.user('trim surface error:', trimerr) totalsurface = self.pos.boundary.volume(self.geom) totalerr = abs(totalsurface - self.exact_totalsurface) / self.exact_totalsurface log.user('total surface error:', totalerr) self.assertLess(trimerr, self.errtol, 'trim surface tolerance not met') self.assertLess(totalerr, self.errtol, 'total surface tolerance not met')
def __exit__(self, type_, value, backtrace): super().__exit__(type_, value, backtrace) if value is not None: self.pvd.close() else: self.pvd.write(' </Collection>\n') self.pvd.write('</VTKFile>\n') self.pvd.close() log.user(self.rootfile)
def step(self, stepdata: StepData): with super().step(stepdata) as step: yield step filename = self.make_filename(with_step=True) writer = self.get_writer() writer.SetFileName(str(filename)) writer.SetInputData(self.grid) writer.Write() log.user(filename)
def __exit__(self, *args, **kwargs): for fname, data in self.field_blocks.items(): with data.blocktype() as fblock: fblock.SetName(fname) for stepid, rblocks in data.steps.items(): fblock.BindResultBlocks(stepid, *rblocks) self.gblock.__exit__(*args, **kwargs) self.exit_stateinfo() self.out.__exit__(*args, **kwargs) super().__exit__(*args, **kwargs) log.user(self.make_filename())
def run_single(self, num, index, namespace): log.user(', '.join(f'{k}={repr(v)}' for k, v in namespace.items())) self.evaluate_context(namespace) namespace['_index'] = num collector = ResultCollector(self._types) for key, value in namespace.items(): collector.collect(key, value) namespace.update(self._constants) with TemporaryDirectory() as workpath: workpath = Path(workpath) if self._logdir: logdir = self.storagepath / render(self._logdir, namespace) logdir.mkdir(parents=True, exist_ok=True) else: logdir = None log.debug( f"Using SRC='{self.sourcepath}', WRK='{workpath}', LOG='{logdir}'" ) for filemap in self._pre_files: filemap.copy(namespace, self.sourcepath, workpath, sourcename='SRC', targetname='WRK') success = True for command in self._commands: if not command.run(collector, namespace, workpath, logdir): self.commit_result(index, collector) success = False break if logdir: for filemap in self._post_files: filemap.copy(namespace, workpath, logdir, sourcename='WRK', targetname='LOG', ignore_missing=not success) self.commit_result(index, collector) return success
def update_geometry(self, geometry: Field, patch: Patch, data: Array2D): if not isinstance(patch.topology, StructuredTopology): raise TypeError("SIMRA writer does not support unstructured grids") nodeshape = tuple(s + 1 for s in patch.topology.shape) data = data.reshape(*nodeshape, 3) data = fix_orientation(data) cellshape = tuple(s - 1 for s in data.shape[:-1]) cells = structured_cells(cellshape, 3) + 1 cells[:, 1], cells[:, 3] = cells[:, 3].copy(), cells[:, 1].copy() cells[:, 5], cells[:, 7] = cells[:, 7].copy(), cells[:, 5].copy() # Compute macro elements rshape = tuple(c - 1 for c in cellshape) mcells = structured_cells(tuple(c - 1 for c in cellshape), 3).reshape( *rshape, -1) + 1 mcells = mcells[::2, ::2, ::2, ...].transpose((1, 0, 2, 3)) mcells = mcells.reshape(-1, 8) mcells[:, 1], mcells[:, 3] = mcells[:, 3].copy(), mcells[:, 1].copy() mcells[:, 5], mcells[:, 7] = mcells[:, 7].copy(), mcells[:, 5].copy() # Write single precision f4_dtype, u4_dtype = dtypes(config.output_endianness) data = data.astype(f4_dtype) cells = cells.astype(u4_dtype) mcells = mcells.astype(u4_dtype) with FortranFile(self.outpath, 'w', header_dtype=u4_dtype) as f: f.write_record( np.array([ data.size // 3, cells.size // 8, data.shape[1], data.shape[0], data.shape[2], mcells.size // 8, ], dtype=u4_dtype)) f.write_record(data.flatten()) f.write_record(cells.flatten()) f.write_record(mcells.flatten()) log.user(self.outpath)
def main(nelems: int, etype: str, btype: str, degree: int, traction: float, maxrefine: int, radius: float, poisson: float): ''' Horizontally loaded linear elastic plate with FCM hole. .. arguments:: nelems [9] Number of elements along edge. etype [square] Type of elements (square/triangle/mixed). btype [std] Type of basis function (std/spline), with availability depending on the selected element type. degree [2] Polynomial degree. traction [.1] Far field traction (relative to Young's modulus). maxrefine [2] Number or refinement levels used for the finite cell method. radius [.5] Cut-out radius. poisson [.3] Poisson's ratio, nonnegative and strictly smaller than 1/2. ''' domain0, geom = mesh.unitsquare(nelems, etype) domain = domain0.trim(function.norm2(geom) - radius, maxrefine=maxrefine) ns = function.Namespace() ns.x = geom ns.lmbda = 2 * poisson ns.mu = 1 - poisson ns.ubasis = domain.basis(btype, degree=degree).vector(2) ns.u_i = 'ubasis_ni ?lhs_n' ns.X_i = 'x_i + u_i' ns.strain_ij = '(d(u_i, x_j) + d(u_j, x_i)) / 2' ns.stress_ij = 'lmbda strain_kk δ_ij + 2 mu strain_ij' ns.r2 = 'x_k x_k' ns.R2 = radius**2 / ns.r2 ns.k = (3 - poisson) / (1 + poisson) # plane stress parameter ns.scale = traction * (1 + poisson) / 2 ns.uexact_i = 'scale (x_i ((k + 1) (0.5 + R2) + (1 - R2) R2 (x_0^2 - 3 x_1^2) / r2) - 2 δ_i1 x_1 (1 + (k - 1 + R2) R2))' ns.du_i = 'u_i - uexact_i' sqr = domain.boundary['left,bottom'].integral('(u_i n_i)^2 J(x)' @ ns, degree=degree * 2) cons = solver.optimize('lhs', sqr, droptol=1e-15) sqr = domain.boundary['top,right'].integral('du_k du_k J(x)' @ ns, degree=20) cons = solver.optimize('lhs', sqr, droptol=1e-15, constrain=cons) res = domain.integral('d(ubasis_ni, x_j) stress_ij J(x)' @ ns, degree=degree * 2) lhs = solver.solve_linear('lhs', res, constrain=cons) bezier = domain.sample('bezier', 5) X, stressxx = bezier.eval(['X', 'stress_00'] @ ns, lhs=lhs) export.triplot('stressxx.png', X, stressxx, tri=bezier.tri, hull=bezier.hull) err = domain.integral('<du_k du_k, sum:ij(d(du_i, x_j)^2)>_n J(x)' @ ns, degree=max(degree, 3) * 2).eval(lhs=lhs)**.5 treelog.user('errors: L2={:.2e}, H1={:.2e}'.format(*err)) return err, cons, lhs
def main(nelems: 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
def main(etype: str, btype: str, degree: int, nrefine: int): ''' Adaptively refined Laplace problem on an L-shaped domain. .. arguments:: etype [square] Type of elements (square/triangle/mixed). btype [h-std] Type of basis function (h/th-std/spline), with availability depending on the configured element type. degree [2] Polynomial degree nrefine [5] Number of refinement steps to perform. ''' domain, geom = mesh.unitsquare(2, etype) x, y = geom - .5 exact = (x**2 + y**2)**(1 / 3) * function.cos( function.arctan2(y + x, y - x) * (2 / 3)) domain = domain.trim(exact - 1e-15, maxrefine=0) linreg = util.linear_regressor() with treelog.iter.fraction('level', range(nrefine + 1)) as lrange: for irefine in lrange: if irefine: refdom = domain.refined ns.refbasis = refdom.basis(btype, degree=degree) indicator = refdom.integral( 'd(refbasis_n, x_k) d(u, x_k) J(x)' @ ns, degree=degree * 2).eval(lhs=lhs) indicator -= refdom.boundary.integral( 'refbasis_n d(u, x_k) n(x_k) J(x)' @ ns, degree=degree * 2).eval(lhs=lhs) supp = ns.refbasis.get_support( indicator**2 > numpy.mean(indicator**2)) domain = domain.refined_by(refdom.transforms[supp]) ns = function.Namespace() ns.x = geom ns.basis = domain.basis(btype, degree=degree) ns.u = 'basis_n ?lhs_n' ns.du = ns.u - exact sqr = domain.boundary['trimmed'].integral('u^2 J(x)' @ ns, degree=degree * 2) cons = solver.optimize('lhs', sqr, droptol=1e-15) sqr = domain.boundary.integral('du^2 J(x)' @ ns, degree=7) cons = solver.optimize('lhs', sqr, droptol=1e-15, constrain=cons) res = domain.integral('d(basis_n, x_k) d(u, x_k) J(x)' @ ns, degree=degree * 2) lhs = solver.solve_linear('lhs', res, constrain=cons) ndofs = len(ns.basis) error = domain.integral('<du^2, sum:k(d(du, x_k)^2)>_i J(x)' @ ns, degree=7).eval(lhs=lhs)**.5 rate, offset = linreg.add(numpy.log(len(ns.basis)), numpy.log(error)) treelog.user( 'ndofs: {ndofs}, L2 error: {error[0]:.2e} ({rate[0]:.2f}), H1 error: {error[1]:.2e} ({rate[1]:.2f})' .format(ndofs=len(ns.basis), error=error, rate=rate)) bezier = domain.sample('bezier', 9) x, u, du = bezier.eval(['x', 'u', 'du'] @ ns, lhs=lhs) export.triplot('sol.png', x, u, tri=bezier.tri, hull=bezier.hull) export.triplot('err.png', x, du, tri=bezier.tri, hull=bezier.hull) return ndofs, error, lhs
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
def test_volume(self): volume = self.pos.volume(self.geom) volerr = abs(volume - self.exact_volume) / self.exact_volume log.user('volume error:', volerr) self.assertLess(volerr, self.errtol, 'volume tolerance not met')
def main(nelems: int, etype: str, btype: str, degree: int, epsilon: typing.Optional[float], contactangle: float, timestep: float, mtol: float, seed: int, circle: bool, stab: stab): ''' Cahn-Hilliard equation on a unit square/circle. .. arguments:: nelems [20] Number of elements along domain edge. etype [square] Type of elements (square/triangle/mixed). btype [std] Type of basis function (std/spline), with availability depending on the configured element type. degree [2] Polynomial degree. epsilon [] Interface thickness; defaults to an automatic value based on the configured mesh density if left unspecified. contactangle [90] Wall contact angle in degrees. timestep [.01] Time step. mtol [.01] Threshold value for chemical potential peak to peak difference, used as a stop criterion. seed [0] Random seed for the initial condition. circle [no] Select circular domain as opposed to a unit square. stab [linear] Stabilization method (linear/optimal/none). ''' mineps = 1. / nelems if epsilon is None: treelog.info('setting epsilon={}'.format(mineps)) epsilon = mineps elif epsilon < mineps: treelog.warning('epsilon under crititical threshold: {} < {}'.format( epsilon, mineps)) domain, geom = mesh.unitsquare(nelems, etype) bezier = domain.sample('bezier', 5) # sample for plotting ns = function.Namespace() if not circle: ns.x = geom else: angle = (geom - .5) * (numpy.pi / 2) ns.x = function.sin(angle) * function.cos(angle)[[1, 0 ]] / numpy.sqrt(2) ns.epsilon = epsilon ns.ewall = .5 * numpy.cos(contactangle * numpy.pi / 180) ns.cbasis = ns.mbasis = domain.basis('std', degree=degree) ns.c = 'cbasis_n ?c_n' ns.dc = 'cbasis_n (?c_n - ?c0_n)' ns.m = 'mbasis_n ?m_n' ns.F = '.5 (c^2 - 1)^2 / epsilon^2' ns.dF = stab.value ns.dt = timestep nrg_mix = domain.integral('F J(x)' @ ns, degree=7) nrg_iface = domain.integral('.5 sum:k(d(c, x_k)^2) J(x)' @ ns, degree=7) nrg_wall = domain.boundary.integral('(abs(ewall) + c ewall) J(x)' @ ns, degree=7) nrg = nrg_mix + nrg_iface + nrg_wall + domain.integral( '(dF - m dc - .5 dt epsilon^2 sum:k(d(m, x_k)^2)) J(x)' @ ns, degree=7) numpy.random.seed(seed) state = dict(c=numpy.random.normal(0, .5, ns.cbasis.shape), m=numpy.random.normal(0, .5, ns.mbasis.shape)) # initial condition with treelog.iter.plain('timestep', itertools.count()) as steps: for istep in steps: E = sample.eval_integrals(nrg_mix, nrg_iface, nrg_wall, **state) treelog.user( 'energy: {0:.3f} ({1[0]:.0f}% mixture, {1[1]:.0f}% interface, {1[2]:.0f}% wall)' .format(sum(E), 100 * numpy.array(E) / sum(E))) x, c, m = bezier.eval(['x', 'c', 'm'] @ ns, **state) export.triplot('phase.png', x, c, tri=bezier.tri, clim=(-1, 1)) export.triplot('chempot.png', x, m, tri=bezier.tri) if numpy.ptp(m) < mtol: break state['c0'] = state['c'] state = solver.optimize(['c', 'm'], nrg, arguments=state, tol=1e-10) return state
def __exit__(self, *args): self.out.__exit__(*args) log.user(self.outpath) return super().__exit__(*args)
return iter(title, builtins.range(*args)) def enumerate(title, iterable): warnings.deprecation('log.enumerate is deprecated; use log.iter.percentage instead') return iter(title, builtins.enumerate(iterable), length=_len(iterable)) def zip(title, *iterables): warnings.deprecation('log.zip is deprecated; use log.iter.percentage instead') return iter(title, builtins.zip(*iterables), length=min(map(_len, iterables))) def count(title, start=0, step=1): warnings.deprecation('log.count is deprecated; use log.iter.percentage instead') return iter(title, itertools.count(start, step)) if distutils.version.StrictVersion(treelog.version) >= distutils.version.StrictVersion('1.0b5'): from treelog import debug, info, user, warning, error, debugfile, infofile, userfile, warningfile, errorfile, context else: debug = lambda *args, **kwargs: treelog.debug(*args, **kwargs) info = lambda *args, **kwargs: treelog.info(*args, **kwargs) user = lambda *args, **kwargs: treelog.user(*args, **kwargs) warning = lambda *args, **kwargs: treelog.warning(*args, **kwargs) error = lambda *args, **kwargs: treelog.error(*args, **kwargs) debugfile = lambda *args, **kwargs: treelog.debugfile(*args, **kwargs) infofile = lambda *args, **kwargs: treelog.infofile(*args, **kwargs) userfile = lambda *args, **kwargs: treelog.userfile(*args, **kwargs) warningfile = lambda *args, **kwargs: treelog.warningfile(*args, **kwargs) errorfile = lambda *args, **kwargs: treelog.errorfile(*args, **kwargs) context = lambda *args, **kwargs: treelog.context(title, *initargs, **initkwargs) # vim:sw=2:sts=2:et