#!/usr/bin/env python3 import os import sys from io import StringIO from optparse import OptionParser import numpy as np import damask scriptName = os.path.splitext(os.path.basename(__file__))[0] scriptID = ' '.join([scriptName,damask.version]) #-------------------------------------------------------------------------------------------------- # MAIN #-------------------------------------------------------------------------------------------------- parser = OptionParser(option_class=damask.extendableOption, usage='%prog options [geomfile(s)]', description = """ Inserts a primitive geometric object at a given position. These objects can be boxes, cylinders, or ellipsoids. """, version = scriptID) parser.add_option('-c', '--center', dest='center', type='float', nargs = 3, metavar=' '.join(['float']*3), help='a,b,c origin of primitive %default') parser.add_option('-d', '--dimension', dest='dimension', type='float', nargs = 3, metavar=' '.join(['float']*3), help='a,b,c extension of hexahedral box') parser.add_option('-e', '--exponent', dest='exponent', type='float', nargs = 3, metavar=' '.join(['float']*3), help='i,j,k exponents for axes: '+ '0 gives octahedron (|x|^(2^0) + |y|^(2^0) + |z|^(2^0) < 1), '+ '1 gives a sphere (|x|^(2^1) + |y|^(2^1) + |z|^(2^1) < 1), '+ 'large values produce boxes, negative turn concave.') parser.add_option('-f', '--fill', dest='fill', type='float', metavar = 'int', help='microstructure index to fill primitive, defaults to max microstructure index + 1') parser.add_option('-q', '--quaternion', dest='quaternion', type='float', nargs = 4, metavar=' '.join(['float']*4), help = 'rotation of primitive as quaternion') parser.add_option('-a', '--angleaxis', dest='angleaxis', type=float, nargs = 4, metavar=' '.join(['float']*4), help = 'axis and angle to rotate primitive') parser.add_option( '--degrees', dest='degrees', action='store_true', help = 'angle is given in degrees') parser.add_option( '--nonperiodic', dest='periodic', action='store_false', help = 'wrap around edges') parser.add_option( '--realspace', dest='realspace', action='store_true', help = '-c and -d span [origin,origin+size] instead of [0,grid] coordinates') parser.add_option( '--invert', dest='inside', action='store_false', help = 'invert the volume filled by the primitive (inside/outside)') parser.set_defaults(center = (.0,.0,.0), degrees = False, exponent = (20,20,20), # box shape by default periodic = True, realspace = False, inside = True, ) (options, filenames) = parser.parse_args() if options.dimension is None: parser.error('no dimension specified.') if [options.angleaxis,options.quaternion].count(None) == 0: parser.error('more than one rotation specified.') if options.angleaxis is not None: rotation = damask.Rotation.fromAxisAngle(np.array(options.angleaxis),options.degrees,normalise=True) elif options.quaternion is not None: rotation = damask.Rotation.fromQuaternion(options.quaternion) else: rotation = damask.Rotation() if filenames == []: filenames = [None] for name in filenames: damask.util.report(scriptName,name) geom = damask.Geom.from_file(StringIO(''.join(sys.stdin.read())) if name is None else name) grid = geom.get_grid() size = geom.get_size() # scale to box of size [1.0,1.0,1.0] if options.realspace: center = (np.array(options.center) - geom.get_origin())/size r = np.array(options.dimension)/size/2.0 else: center = (np.array(options.center) + 0.5)/grid r = np.array(options.dimension)/grid/2.0 if np.any(center<0.0) or np.any(center>=1.0): print('error') offset = np.ones(3)*0.5 if options.periodic else center mask = np.full(grid,False) # High exponents can cause underflow & overflow - okay here, just compare to 1, so +infinity and 0 are fine np.seterr(over='ignore', under='ignore') e = 2.0**np.array(options.exponent) for x in range(grid[0]): for y in range(grid[1]): for z in range(grid[2]): coords = np.array([x+0.5,y+0.5,z+0.5])/grid mask[x,y,z] = np.sum(np.abs((rotation*(coords-offset))/r)**e) < 1 if options.periodic: shift = ((offset-center)*grid).astype(int) mask = np.roll(mask,shift,(0,1,2)) if options.inside: mask = np.logical_not(mask) fill = np.nanmax(geom.microstructure)+1 if options.fill is None else options.fill damask.util.croak(geom.update(np.where(mask,geom.microstructure,fill))) geom.add_comments(scriptID + ' ' + ' '.join(sys.argv[1:])) geom.to_file(sys.stdout if name is None else name,pack=False)