diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ed5e762b7..bb4a2e7a0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -25,6 +25,7 @@ before_script: fi - while [ $(awk "/$CI_PIPELINE_ID/{print NR}" $TESTROOT/GitLabCI.queue) != 1 ]; do sleep 5m; + echo -e "Currently queued pipelines:\n$(cat $TESTROOT/GitLabCI.queue)\n"; done - source $DAMASKROOT/env/DAMASK.sh - cd $DAMASKROOT/PRIVATE/testing @@ -87,6 +88,7 @@ checkout: - echo $CI_PIPELINE_ID >> $TESTROOT/GitLabCI.queue - while [ $(awk "/$CI_PIPELINE_ID/{print NR}" $TESTROOT/GitLabCI.queue) != 1 ]; do sleep 5m; + echo -e "Currently queued pipelines:\n$(cat $TESTROOT/GitLabCI.queue)\n"; done script: - mkdir -p $DAMASKROOT diff --git a/PRIVATE b/PRIVATE index 555f3e01f..fa2885da8 160000 --- a/PRIVATE +++ b/PRIVATE @@ -1 +1 @@ -Subproject commit 555f3e01f2b5cf43ade1bd48423b890adca21771 +Subproject commit fa2885da8604c3ac46f64670393f04678dd1f45b diff --git a/VERSION b/VERSION index bebbc79d0..a05ebe3cf 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -v3.0.0-alpha-233-g190f8a82 +v3.0.0-alpha-275-g7801f527f diff --git a/processing/legacy/addAPS34IDEstrainCoords.py b/processing/legacy/addAPS34IDEstrainCoords.py index 9ba51ada1..465f03e4e 100755 --- a/processing/legacy/addAPS34IDEstrainCoords.py +++ b/processing/legacy/addAPS34IDEstrainCoords.py @@ -42,11 +42,10 @@ rot_to_TSL = damask.Rotation.from_axis_angle([-1,0,0,.75*np.pi]) for name in filenames: damask.util.report(scriptName,name) - table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) - + table = damask.Table.load(StringIO(''.join(sys.stdin.read())) if name is None else name) + coord = - table.get(options.frame) coord[:,2] += table.get(options.depth)[:,0] - table.add('coord',rot_to_TSL.broadcast_to(coord.shape[0]) @ coord,scriptID+' '+' '.join(sys.argv[1:])) - - table.to_file(sys.stdout if name is None else name) + table.add('coord',rot_to_TSL.broadcast_to(coord.shape[0]) @ coord,scriptID+' '+' '.join(sys.argv[1:]))\ + .save((sys.stdout if name is None else name),legacy=True) diff --git a/processing/legacy/addCumulative.py b/processing/legacy/addCumulative.py index f84828a89..3ba527acd 100755 --- a/processing/legacy/addCumulative.py +++ b/processing/legacy/addCumulative.py @@ -39,10 +39,10 @@ if options.labels is None: for name in filenames: damask.util.report(scriptName,name) - table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + table = damask.Table.load(StringIO(''.join(sys.stdin.read())) if name is None else name) for label in options.labels: - table.add('cum_{}({})'.format('prod' if options.product else 'sum',label), - np.cumprod(table.get(label),0) if options.product else np.cumsum(table.get(label),0), - scriptID+' '+' '.join(sys.argv[1:])) + table = table.add('cum_{}({})'.format('prod' if options.product else 'sum',label), + np.cumprod(table.get(label),0) if options.product else np.cumsum(table.get(label),0), + scriptID+' '+' '.join(sys.argv[1:])) - table.to_file(sys.stdout if name is None else name) + table.save((sys.stdout if name is None else name),legacy=True) diff --git a/processing/post/DADF5_postResults.py b/processing/post/DADF5_postResults.py index e81330581..02eb72d87 100755 --- a/processing/post/DADF5_postResults.py +++ b/processing/post/DADF5_postResults.py @@ -38,8 +38,8 @@ for filename in options.filenames: N_digits = int(np.floor(np.log10(int(results.increments[-1][3:]))))+1 N_digits = 5 # hack to keep test intact for inc in damask.util.show_progress(results.iterate('increments'),len(results.increments)): - table = damask.Table(np.ones(np.product(results.grid),dtype=int)*int(inc[3:]),{'inc':(1,)}) - table = table.add('pos',coords.reshape(-1,3)) + table = damask.Table(np.ones(np.product(results.grid),dtype=int)*int(inc[3:]),{'inc':(1,)})\ + .add('pos',coords.reshape(-1,3)) results.pick('materialpoints',False) results.pick('constituents', True) @@ -60,4 +60,4 @@ for filename in options.filenames: os.mkdir(dirname,0o755) file_out = '{}_inc{}.txt'.format(os.path.splitext(os.path.split(filename)[-1])[0], inc[3:].zfill(N_digits)) - table.to_file(os.path.join(dirname,file_out)) + table.save(os.path.join(dirname,file_out),legacy=True) diff --git a/processing/post/addCompatibilityMismatch.py b/processing/post/addCompatibilityMismatch.py index 5009d44a0..0e7d3ea42 100755 --- a/processing/post/addCompatibilityMismatch.py +++ b/processing/post/addCompatibilityMismatch.py @@ -172,7 +172,7 @@ if filenames == []: filenames = [None] for name in filenames: damask.util.report(scriptName,name) - table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + table = damask.Table.load(StringIO(''.join(sys.stdin.read())) if name is None else name) grid,size,origin = damask.grid_filters.cell_coord0_gridSizeOrigin(table.get(options.pos)) F = table.get(options.defgrad).reshape(tuple(grid)+(-1,),order='F').reshape(tuple(grid)+(3,3)) @@ -191,4 +191,4 @@ for name in filenames: volumeMismatch.reshape(-1,1,order='F'), scriptID+' '+' '.join(sys.argv[1:])) - table.to_file(sys.stdout if name is None else name) + table.save((sys.stdout if name is None else name), legacy=True) diff --git a/processing/post/addCurl.py b/processing/post/addCurl.py index 1033e3303..699fc945f 100755 --- a/processing/post/addCurl.py +++ b/processing/post/addCurl.py @@ -43,7 +43,7 @@ if options.labels is None: parser.error('no data column specified.') for name in filenames: damask.util.report(scriptName,name) - table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + table = damask.Table.load(StringIO(''.join(sys.stdin.read())) if name is None else name) grid,size,origin = damask.grid_filters.cell_coord0_gridSizeOrigin(table.get(options.pos)) for label in options.labels: @@ -55,4 +55,4 @@ for name in filenames: curl.reshape(tuple(grid)+(-1,)).reshape(-1,np.prod(shape),order='F'), scriptID+' '+' '.join(sys.argv[1:])) - table.to_file(sys.stdout if name is None else name) + table.save((sys.stdout if name is None else name), legacy=True) diff --git a/processing/post/addDerivative.py b/processing/post/addDerivative.py index b6b19c98a..99016f4ef 100755 --- a/processing/post/addDerivative.py +++ b/processing/post/addDerivative.py @@ -14,9 +14,9 @@ scriptName = os.path.splitext(os.path.basename(__file__))[0] scriptID = ' '.join([scriptName,damask.version]) def derivative(coordinates,what): - + result = np.empty_like(what) - + # use differentiation by interpolation # as described in http://www2.math.umd.edu/~dlevy/classes/amsc466/lecture-notes/differentiation-chap.pdf @@ -31,7 +31,7 @@ def derivative(coordinates,what): (coordinates[0] - coordinates[1]) result[-1,:] = (what[-1,:] - what[-2,:]) / \ (coordinates[-1] - coordinates[-2]) - + return result @@ -65,10 +65,10 @@ if options.labels is None: for name in filenames: damask.util.report(scriptName,name) - table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + table = damask.Table.load(StringIO(''.join(sys.stdin.read())) if name is None else name) for label in options.labels: table = table.add('d({})/d({})'.format(label,options.coordinates), derivative(table.get(options.coordinates),table.get(label)), scriptID+' '+' '.join(sys.argv[1:])) - table.to_file(sys.stdout if name is None else name) + table.save((sys.stdout if name is None else name), legacy=True) diff --git a/processing/post/addDisplacement.py b/processing/post/addDisplacement.py index f1ab565b0..a6cff86ab 100755 --- a/processing/post/addDisplacement.py +++ b/processing/post/addDisplacement.py @@ -47,25 +47,25 @@ parser.set_defaults(f = 'f', for name in filenames: damask.util.report(scriptName,name) - table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + table = damask.Table.load(StringIO(''.join(sys.stdin.read())) if name is None else name) grid,size,origin = damask.grid_filters.cell_coord0_gridSizeOrigin(table.get(options.pos)) F = table.get(options.f).reshape(tuple(grid)+(-1,),order='F').reshape(tuple(grid)+(3,3)) if options.nodal: - table = damask.Table(damask.grid_filters.node_coord0(grid,size).reshape(-1,3,order='F'), + damask.Table(damask.grid_filters.node_coord0(grid,size).reshape(-1,3,order='F'), {'pos':(3,)})\ .add('avg({}).{}'.format(options.f,options.pos), damask.grid_filters.node_displacement_avg(size,F).reshape(-1,3,order='F'), scriptID+' '+' '.join(sys.argv[1:]))\ .add('fluct({}).{}'.format(options.f,options.pos), damask.grid_filters.node_displacement_fluct(size,F).reshape(-1,3,order='F'), - scriptID+' '+' '.join(sys.argv[1:])) - table.to_file(sys.stdout if name is None else os.path.splitext(name)[0]+'_nodal.txt') + scriptID+' '+' '.join(sys.argv[1:]))\ + .save((sys.stdout if name is None else os.path.splitext(name)[0]+'_nodal.txt'), legacy=True) else: - table = table.add('avg({}).{}'.format(options.f,options.pos), + table.add('avg({}).{}'.format(options.f,options.pos), damask.grid_filters.cell_displacement_avg(size,F).reshape(-1,3,order='F'), scriptID+' '+' '.join(sys.argv[1:]))\ .add('fluct({}).{}'.format(options.f,options.pos), damask.grid_filters.cell_displacement_fluct(size,F).reshape(-1,3,order='F'), - scriptID+' '+' '.join(sys.argv[1:])) - table.to_file(sys.stdout if name is None else name) + scriptID+' '+' '.join(sys.argv[1:]))\ + .save((sys.stdout if name is None else name), legacy=True) diff --git a/processing/post/addDivergence.py b/processing/post/addDivergence.py index 6495793cf..208a0f7b6 100755 --- a/processing/post/addDivergence.py +++ b/processing/post/addDivergence.py @@ -43,7 +43,7 @@ if options.labels is None: parser.error('no data column specified.') for name in filenames: damask.util.report(scriptName,name) - table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + table = damask.Table.load(StringIO(''.join(sys.stdin.read())) if name is None else name) grid,size,origin = damask.grid_filters.cell_coord0_gridSizeOrigin(table.get(options.pos)) for label in options.labels: @@ -55,4 +55,4 @@ for name in filenames: div.reshape(tuple(grid)+(-1,)).reshape(-1,np.prod(shape)//3,order='F'), scriptID+' '+' '.join(sys.argv[1:])) - table.to_file(sys.stdout if name is None else name) + table.save((sys.stdout if name is None else name), legacy=True) diff --git a/processing/post/addEuclideanDistance.py b/processing/post/addEuclideanDistance.py index f5cf58ab3..fc43542bd 100755 --- a/processing/post/addEuclideanDistance.py +++ b/processing/post/addEuclideanDistance.py @@ -142,7 +142,7 @@ for i,feature in enumerate(features): for name in filenames: damask.util.report(scriptName,name) - table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + table = damask.Table.load(StringIO(''.join(sys.stdin.read())) if name is None else name) grid,size,origin = damask.grid_filters.cell_coord0_gridSizeOrigin(table.get(options.pos)) neighborhood = neighborhoods[options.neighborhood] @@ -158,7 +158,7 @@ for name in filenames: diffToNeighbor[:,:,:,i] = ndimage.convolve(microstructure,stencil) # compare ID at each point... # ...to every one in the specified neighborhood # for same IDs at both locations ==> 0 - + diffToNeighbor = np.sort(diffToNeighbor) # sort diff such that number of changes in diff (steps)... # ...reflects number of unique neighbors uniques = np.where(diffToNeighbor[1:-1,1:-1,1:-1,0] != 0, 1,0) # initialize unique value counter (exclude myself [= 0]) @@ -184,4 +184,4 @@ for name in filenames: distance[i,:], scriptID+' '+' '.join(sys.argv[1:])) - table.to_file(sys.stdout if name is None else name) + table.save((sys.stdout if name is None else name), legacy=True) diff --git a/processing/post/addGaussian.py b/processing/post/addGaussian.py index 8e58da884..f00122c63 100755 --- a/processing/post/addGaussian.py +++ b/processing/post/addGaussian.py @@ -63,7 +63,7 @@ if options.labels is None: parser.error('no data column specified.') for name in filenames: damask.util.report(scriptName,name) - table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + table = damask.Table.load(StringIO(''.join(sys.stdin.read())) if name is None else name) damask.grid_filters.coord0_check(table.get(options.pos)) for label in options.labels: @@ -73,4 +73,4 @@ for name in filenames: mode = 'wrap' if options.periodic else 'nearest'), scriptID+' '+' '.join(sys.argv[1:])) - table.to_file(sys.stdout if name is None else name) + table.save((sys.stdout if name is None else name), legacy=True) diff --git a/processing/post/addGradient.py b/processing/post/addGradient.py index 718a972f3..d049b65d7 100755 --- a/processing/post/addGradient.py +++ b/processing/post/addGradient.py @@ -43,7 +43,7 @@ if options.labels is None: parser.error('no data column specified.') for name in filenames: damask.util.report(scriptName,name) - table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + table = damask.Table.load(StringIO(''.join(sys.stdin.read())) if name is None else name) grid,size,origin = damask.grid_filters.cell_coord0_gridSizeOrigin(table.get(options.pos)) for label in options.labels: @@ -55,4 +55,4 @@ for name in filenames: grad.reshape(tuple(grid)+(-1,)).reshape(-1,np.prod(shape)*3,order='F'), scriptID+' '+' '.join(sys.argv[1:])) - table.to_file(sys.stdout if name is None else name) + table.save((sys.stdout if name is None else name), legacy=True) diff --git a/processing/post/addOrientations.py b/processing/post/addOrientations.py index dddc14193..6a02cca08 100755 --- a/processing/post/addOrientations.py +++ b/processing/post/addOrientations.py @@ -110,7 +110,7 @@ R = damask.Rotation.from_axis_angle(np.array(options.labrotation),options.degree for name in filenames: damask.util.report(scriptName,name) - table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + table = damask.Table.load(StringIO(''.join(sys.stdin.read())) if name is None else name) if options.eulers is not None: label = options.eulers @@ -147,4 +147,4 @@ for name in filenames: if 'axisangle' in options.output: table = table.add('om({})'.format(label),o.as_axisangle(options.degrees), scriptID+' '+' '.join(sys.argv[1:])) - table.to_file(sys.stdout if name is None else name) + table.save((sys.stdout if name is None else name), legacy=True) diff --git a/processing/post/addSchmidfactors.py b/processing/post/addSchmidfactors.py index dc4117d78..8f43308cb 100755 --- a/processing/post/addSchmidfactors.py +++ b/processing/post/addSchmidfactors.py @@ -175,7 +175,7 @@ labels = ['S[{direction[0]:.1g}_{direction[1]:.1g}_{direction[2]:.1g}]' for name in filenames: damask.util.report(scriptName,name) - table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + table = damask.Table.load(StringIO(''.join(sys.stdin.read())) if name is None else name) o = damask.Rotation.from_quaternion(table.get(options.quaternion)) @@ -189,4 +189,4 @@ for name in filenames: for i,label in enumerate(labels): table = table.add(label,S[:,i],scriptID+' '+' '.join(sys.argv[1:])) - table.to_file(sys.stdout if name is None else name) + table.save((sys.stdout if name is None else name), legacy=True) diff --git a/processing/post/filterTable.py b/processing/post/filterTable.py index 494257a60..4f4af088b 100755 --- a/processing/post/filterTable.py +++ b/processing/post/filterTable.py @@ -27,7 +27,7 @@ def sortingList(labels,whitelistitems): else: indices.append(0) names.append(label) - + return [indices,names,whitelistitems] @@ -72,11 +72,11 @@ for name in filenames: continue damask.util.report(scriptName,name) -# ------------------------------------------ assemble info --------------------------------------- +# ------------------------------------------ assemble info --------------------------------------- table.head_read() -# ------------------------------------------ process data --------------------------------------- +# ------------------------------------------ process data --------------------------------------- specials = { \ '_row_': 0, @@ -103,12 +103,12 @@ for name in filenames: else np.lexsort(sortingList(labels,whitelistitem)) # reorder if unique, i.e. no "-1" in whitelistitem else: order = range(len(labels)) # maintain original order of labels - + # --------------------------------------- evaluate condition --------------------------------------- if options.condition is not None: condition = options.condition # copy per file, since might be altered inline breaker = False - + for position,(all,marker,column) in enumerate(set(re.findall(r'#(([s]#)?(.+?))#',condition))): # find three groups idx = table.label_index(column) dim = table.label_dimension(column) @@ -123,11 +123,11 @@ for name in filenames: 's#':'str'}[marker],idx) # take float or string value of data column elif dim > 1: # multidimensional input (vector, tensor, etc.) replacement = 'np.array(table.data[{}:{}],dtype=float)'.format(idx,idx+dim) # use (flat) array representation - + condition = condition.replace('#'+all+'#',replacement) - + if breaker: continue # found mistake in condition evaluation --> next file - + # ------------------------------------------ assemble header --------------------------------------- table.info_append(scriptID + '\t' + ' '.join(sys.argv[1:])) @@ -138,7 +138,7 @@ for name in filenames: # ------------------------------------------ process and output data ------------------------------------------ positions = np.array(positions)[order] - + atOnce = options.condition is None if atOnce: # read full array and filter columns try: diff --git a/processing/post/permuteData.py b/processing/post/permuteData.py index 316fdd3da..073ccfd9f 100755 --- a/processing/post/permuteData.py +++ b/processing/post/permuteData.py @@ -47,7 +47,7 @@ if filenames == []: filenames = [None] for name in filenames: damask.util.report(scriptName,name) - table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + table = damask.Table.load(StringIO(''.join(sys.stdin.read())) if name is None else name) randomSeed = int(os.urandom(4).hex(), 16) if options.randomSeed is None else options.randomSeed # random seed per file rng = np.random.default_rng(randomSeed) @@ -58,4 +58,4 @@ for name in filenames: rng.shuffle(uniques) table = table.set(label,uniques[inverse], scriptID+' '+' '.join(sys.argv[1:])) - table.to_file(sys.stdout if name is None else name) + table.save((sys.stdout if name is None else name), legacy=True) diff --git a/processing/pre/geom_fromDREAM3D.py b/processing/pre/geom_fromDREAM3D.py index 6b7ccc21a..3faa07a17 100755 --- a/processing/pre/geom_fromDREAM3D.py +++ b/processing/pre/geom_fromDREAM3D.py @@ -154,4 +154,4 @@ for name in filenames: homogenization=options.homogenization,comments=header) damask.util.croak(geom) - geom.to_file(os.path.splitext(name)[0]+'.geom',format='ASCII',pack=False) + geom.save_ASCII(os.path.splitext(name)[0]+'.geom',compress=False) diff --git a/processing/pre/geom_fromMinimalSurface.py b/processing/pre/geom_fromMinimalSurface.py index e6289ba0a..b52003d79 100755 --- a/processing/pre/geom_fromMinimalSurface.py +++ b/processing/pre/geom_fromMinimalSurface.py @@ -89,4 +89,4 @@ geom=damask.Geom(microstructure,options.size, comments=[scriptID + ' ' + ' '.join(sys.argv[1:])]) damask.util.croak(geom) -geom.to_file(sys.stdout if name is None else name,format='ASCII',pack=False) +geom.save_ASCII(sys.stdout if name is None else name,compress=False) diff --git a/processing/pre/geom_fromOsteonGeometry.py b/processing/pre/geom_fromOsteonGeometry.py index 6c6326163..0b1440d63 100755 --- a/processing/pre/geom_fromOsteonGeometry.py +++ b/processing/pre/geom_fromOsteonGeometry.py @@ -142,4 +142,4 @@ geom = damask.Geom(microstructure.reshape(grid), homogenization=options.homogenization,comments=header) damask.util.croak(geom) -geom.to_file(sys.stdout if name is None else name,format='ASCII',pack=False) +geom.save_ASCII(sys.stdout if name is None else name,compress=False) diff --git a/processing/pre/geom_fromTable.py b/processing/pre/geom_fromTable.py index dc968b82e..6f2cb5b4d 100755 --- a/processing/pre/geom_fromTable.py +++ b/processing/pre/geom_fromTable.py @@ -68,7 +68,7 @@ if options.axes is not None and not set(options.axes).issubset(set(['x','+x','-x for name in filenames: damask.util.report(scriptName,name) - table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + table = damask.Table.load(StringIO(''.join(sys.stdin.read())) if name is None else name) table.sort_by(['{}_{}'.format(i,options.pos) for i in range(3,0,-1)]) # x fast, y slow grid,size,origin = damask.grid_filters.cell_coord0_gridSizeOrigin(table.get(options.pos)) @@ -105,5 +105,4 @@ for name in filenames: homogenization=options.homogenization,comments=header) damask.util.croak(geom) - geom.to_file(sys.stdout if name is None else os.path.splitext(name)[0]+'.geom', - format='ASCII',pack=False) + geom.save_ASCII(sys.stdout if name is None else os.path.splitext(name)[0]+'.geom',compress=False) diff --git a/processing/pre/geom_fromVoronoiTessellation.py b/processing/pre/geom_fromVoronoiTessellation.py index 67a0dfecc..5586d3bc8 100755 --- a/processing/pre/geom_fromVoronoiTessellation.py +++ b/processing/pre/geom_fromVoronoiTessellation.py @@ -171,7 +171,7 @@ if filenames == []: filenames = [None] for name in filenames: damask.util.report(scriptName,name) - table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + table = damask.Table.load(StringIO(''.join(sys.stdin.read())) if name is None else name) size = np.ones(3) origin = np.zeros(3) @@ -228,5 +228,4 @@ for name in filenames: homogenization=options.homogenization,comments=header) damask.util.croak(geom) - geom.to_file(sys.stdout if name is None else os.path.splitext(name)[0]+'.geom', - format='ASCII',pack=False) + geom.save_ASCII(sys.stdout if name is None else os.path.splitext(name)[0]+'.geom',compress=False) diff --git a/processing/pre/geom_grainGrowth.py b/processing/pre/geom_grainGrowth.py index ea9331317..88e75a841 100755 --- a/processing/pre/geom_grainGrowth.py +++ b/processing/pre/geom_grainGrowth.py @@ -62,7 +62,7 @@ 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) + geom = damask.Geom.load_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) grid_original = geom.grid damask.util.croak(geom) @@ -169,7 +169,7 @@ for name in filenames: # undo any changes involving immutable microstructures microstructure = np.where(immutable, microstructure_original,microstructure) - geom=geom.duplicate(microstructure[0:grid_original[0],0:grid_original[1],0:grid_original[2]]) + geom = geom.duplicate(microstructure[0:grid_original[0],0:grid_original[1],0:grid_original[2]]) geom.add_comments(scriptID + ' ' + ' '.join(sys.argv[1:])) - geom.to_file(sys.stdout if name is None else name,format='ASCII',pack=False) + geom.save_ASCII(sys.stdout if name is None else name,compress=False) diff --git a/processing/pre/hybridIA_linODFsampling.py b/processing/pre/hybridIA_linODFsampling.py index 6f5827f8b..f99a2dd89 100755 --- a/processing/pre/hybridIA_linODFsampling.py +++ b/processing/pre/hybridIA_linODFsampling.py @@ -31,7 +31,7 @@ def binAsBins(bin,intervals): bins[1] = (bin//intervals[2]) % intervals[1] bins[2] = bin % intervals[2] return bins - + def binsAsBin(bins,intervals): """Implode 3D bins into compound bin.""" return (bins[0]*intervals[1] + bins[1])*intervals[2] + bins[2] @@ -95,7 +95,7 @@ def directInversion (ODF,nSamples): float(nInvSamples)/nOptSamples-1.0, scale,nSamples)) repetition = [None]*ODF['nBins'] # preallocate and clear - + for bin in range(ODF['nBins']): # loop over bins repetition[bin] = int(round(ODF['dV_V'][bin]*scale)) # calc repetition @@ -105,7 +105,7 @@ def directInversion (ODF,nSamples): for bin in range(ODF['nBins']): set[i:i+repetition[bin]] = [bin]*repetition[bin] # fill set with bin, i.e. orientation i += repetition[bin] # advance set counter - + orientations = np.zeros((nSamples,3),'f') reconstructedODF = np.zeros(ODF['nBins'],'f') unitInc = 1.0/nSamples @@ -117,7 +117,7 @@ def directInversion (ODF,nSamples): orientations[j] = np.degrees(Eulers) reconstructedODF[bin] += unitInc set[ex] = set[j] # exchange orientations - + return orientations, reconstructedODF @@ -130,7 +130,7 @@ def MonteCarloEulers (ODF,nSamples): orientations = np.zeros((nSamples,3),'f') reconstructedODF = np.zeros(ODF['nBins'],'f') unitInc = 1.0/nSamples - + for j in range(nSamples): MC = maxdV_V*2.0 bin = 0 @@ -153,7 +153,7 @@ def MonteCarloBins (ODF,nSamples): orientations = np.zeros((nSamples,3),'f') reconstructedODF = np.zeros(ODF['nBins'],'f') unitInc = 1.0/nSamples - + for j in range(nSamples): MC = maxdV_V*2.0 bin = 0 @@ -173,14 +173,14 @@ def TothVanHoutteSTAT (ODF,nSamples): orientations = np.zeros((nSamples,3),'f') reconstructedODF = np.zeros(ODF['nBins'],'f') unitInc = 1.0/nSamples - + selectors = [random.random() for i in range(nSamples)] selectors.sort() indexSelector = 0 - + cumdV_V = 0.0 countSamples = 0 - + for bin in range(ODF['nBins']) : cumdV_V += ODF['dV_V'][bin] while indexSelector < nSamples and selectors[indexSelector] < cumdV_V: @@ -191,7 +191,7 @@ def TothVanHoutteSTAT (ODF,nSamples): indexSelector += 1 damask.util.croak('created set of %i when asked to deliver %i'%(countSamples,nSamples)) - + return orientations, reconstructedODF @@ -233,8 +233,8 @@ if filenames == []: filenames = [None] for name in filenames: damask.util.report(scriptName,name) - - table = damask.Table.from_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + + table = damask.Table.load(StringIO(''.join(sys.stdin.read())) if name is None else name) randomSeed = int(os.urandom(4).hex(),16) if options.randomSeed is None else options.randomSeed # random seed per file random.seed(randomSeed) @@ -253,7 +253,7 @@ for name in filenames: if eulers.shape[0] != ODF['nBins']: damask.util.croak('expecting %i values but got %i'%(ODF['nBins'],eulers.shape[0])) continue - + # ----- build binnedODF array and normalize ------------------------------------------------------ sumdV_V = 0.0 ODF['dV_V'] = [None]*ODF['nBins'] @@ -267,7 +267,7 @@ for name in filenames: if ODF['dV_V'][b] > 0.0: sumdV_V += ODF['dV_V'][b] ODF['nNonZero'] += 1 - + for b in range(ODF['nBins']): ODF['dV_V'][b] /= sumdV_V # normalize dV/V @@ -277,19 +277,19 @@ for name in filenames: 'Volume integral of ODF: %12.11f\n'%sumdV_V, 'Reference Integral: %12.11f\n'%(ODF['limit'][0]*ODF['limit'][2]*(1-math.cos(ODF['limit'][1]))), ]) - + Functions = {'IA': 'directInversion', 'STAT': 'TothVanHoutteSTAT', 'MC': 'MonteCarloBins'} method = Functions[options.algorithm] Orientations, ReconstructedODF = (globals()[method])(ODF,options.number) - + # calculate accuracy of sample squaredDiff = {'orig':0.0,method:0.0} squaredRelDiff = {'orig':0.0,method:0.0} mutualProd = {'orig':0.0,method:0.0} indivSum = {'orig':0.0,method:0.0} indivSquaredSum = {'orig':0.0,method:0.0} - + for bin in range(ODF['nBins']): squaredDiff[method] += (ODF['dV_V'][bin] - ReconstructedODF[bin])**2 if ODF['dV_V'][bin] > 0.0: @@ -299,7 +299,7 @@ for name in filenames: indivSquaredSum[method] += ReconstructedODF[bin]**2 indivSum['orig'] += ODF['dV_V'][bin] indivSquaredSum['orig'] += ODF['dV_V'][bin]**2 - + damask.util.croak(['sqrt(N*)RMSD of ODFs:\t %12.11f'% math.sqrt(options.number*squaredDiff[method]), 'RMSrD of ODFs:\t %12.11f'%math.sqrt(squaredRelDiff[method]), 'rMSD of ODFs:\t %12.11f'%(squaredDiff[method]/indivSquaredSum['orig']), @@ -311,10 +311,10 @@ for name in filenames: (ODF['nNonZero']*math.sqrt((indivSquaredSum['orig']/ODF['nNonZero']-(indivSum['orig']/ODF['nNonZero'])**2)*\ (indivSquaredSum[method]/ODF['nNonZero']-(indivSum[method]/ODF['nNonZero'])**2)))), ]) - + if method == 'IA' and options.number < ODF['nNonZero']: strOpt = '(%i)'%ODF['nNonZero'] - + formatwidth = 1+int(math.log10(options.number)) materialConfig = [ @@ -324,12 +324,12 @@ for name in filenames: '', '#-------------------#', ] - + for i,ID in enumerate(range(options.number)): materialConfig += ['[Grain%s]'%(str(ID+1).zfill(formatwidth)), '(constituent) phase %i texture %s fraction 1.0'%(options.phase,str(ID+1).rjust(formatwidth)), ] - + materialConfig += [ '#-------------------#', '', @@ -338,12 +338,12 @@ for name in filenames: for ID in range(options.number): eulers = Orientations[ID] - + materialConfig += ['[Grain%s]'%(str(ID+1).zfill(formatwidth)), '(gauss) phi1 {} Phi {} phi2 {} scatter 0.0 fraction 1.0'.format(*eulers), ] -#--- output finalization -------------------------------------------------------------------------- +#--- output finalization -------------------------------------------------------------------------- with (open(os.path.splitext(name)[0]+'_'+method+'_'+str(options.number)+'_material.config','w')) as outfile: outfile.write('\n'.join(materialConfig)+'\n') diff --git a/processing/pre/mentat_spectralBox.py b/processing/pre/mentat_spectralBox.py index 76649aa1a..98703219f 100755 --- a/processing/pre/mentat_spectralBox.py +++ b/processing/pre/mentat_spectralBox.py @@ -130,10 +130,10 @@ def geometry(): #------------------------------------------------------------------------------------------------- -def initial_conditions(microstructures): +def initial_conditions(materials): elements = [] element = 0 - for id in microstructures: + for id in materials: element += 1 if len(elements) < id: for i in range(id-len(elements)): @@ -153,7 +153,7 @@ def initial_conditions(microstructures): for grain,elementList in enumerate(elements): cmds.append([\ "*new_icond", - "*icond_name microstructure_%i"%(grain+1), + "*icond_name material_%i"%(grain+1), "*icond_type state_variable", "*icond_param_value state_var_id 2", "*icond_dof_value var %i"%(grain+1), @@ -196,15 +196,15 @@ 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) - microstructure = geom.microstructure.flatten(order='F') + geom = damask.Geom.load_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + materials = geom.materials.flatten(order='F') cmds = [\ init(), mesh(geom.grid,geom.size), material(), geometry(), - initial_conditions(microstructure), + initial_conditions(materials), '*identify_sets', '*show_model', '*redraw', diff --git a/processing/pre/seeds_fromDistribution.py b/processing/pre/seeds_fromDistribution.py index 1a4ec6971..e1e4726c2 100755 --- a/processing/pre/seeds_fromDistribution.py +++ b/processing/pre/seeds_fromDistribution.py @@ -78,7 +78,7 @@ class myThread (threading.Thread): perturbedSeedsVFile = StringIO() myBestSeedsVFile.seek(0) - perturbedSeedsTable = damask.Table.from_ASCII(myBestSeedsVFile) + perturbedSeedsTable = damask.Table.load(myBestSeedsVFile) coords = perturbedSeedsTable.get('pos') i = 0 for ms,coord in enumerate(coords): @@ -89,8 +89,7 @@ class myThread (threading.Thread): coords[i]=newCoords direction[i]*=2. i+= 1 - perturbedSeedsTable.set('pos',coords) - perturbedSeedsTable.to_file(perturbedSeedsVFile) + perturbedSeedsTable.set('pos',coords).save(perturbedSeedsVFile,legacy=True) #--- do tesselation with perturbed seed file ------------------------------------------------------ perturbedGeomVFile.close() @@ -101,7 +100,7 @@ class myThread (threading.Thread): perturbedGeomVFile.seek(0) #--- evaluate current seeds file ------------------------------------------------------------------ - perturbedGeom = damask.Geom.from_file(perturbedGeomVFile) + perturbedGeom = damask.Geom.load_ASCII(perturbedGeomVFile) myNmicrostructures = len(np.unique(perturbedGeom.microstructure)) currentData=np.bincount(perturbedGeom.microstructure.ravel())[1:]/points currentError=[] @@ -213,14 +212,14 @@ if options.randomSeed is None: options.randomSeed = int(os.urandom(4).hex(),16) damask.util.croak(options.randomSeed) delta = options.scale/np.array(options.grid) -baseFile=os.path.splitext(os.path.basename(options.seedFile))[0] +baseFile = os.path.splitext(os.path.basename(options.seedFile))[0] points = np.array(options.grid).prod().astype('float') # ----------- calculate target distribution and bin edges -targetGeom = damask.Geom.from_file(os.path.splitext(os.path.basename(options.target))[0]+'.geom') +targetGeom = damask.Geom.load_ASCII(os.path.splitext(os.path.basename(options.target))[0]+'.geom') nMicrostructures = len(np.unique(targetGeom.microstructure)) targetVolFrac = np.bincount(targetGeom.microstructure.flatten())/targetGeom.grid.prod().astype(np.float) -target=[] +target = [] for i in range(1,nMicrostructures+1): targetHist,targetBins = np.histogram(targetVolFrac,bins=i) #bin boundaries target.append({'histogram':targetHist,'bins':targetBins}) @@ -243,7 +242,7 @@ initialGeomVFile = StringIO() initialGeomVFile.write(damask.util.execute('geom_fromVoronoiTessellation '+ ' -g '+' '.join(list(map(str, options.grid))),bestSeedsVFile)[0]) initialGeomVFile.seek(0) -initialGeom = damask.Geom.from_file(initialGeomVFile) +initialGeom = damask.Geom.load_ASCII(initialGeomVFile) if len(np.unique(targetGeom.microstructure)) != nMicrostructures: damask.util.croak('error. Microstructure count mismatch') @@ -273,8 +272,8 @@ sys.stdout.flush() initialGeomVFile.close() # start mulithreaded monte carlo simulation -threads=[] -s=threading.Semaphore(1) +threads = [] +s = threading.Semaphore(1) for i in range(options.threads): threads.append(myThread(i)) diff --git a/processing/pre/seeds_fromGeom.py b/processing/pre/seeds_fromGeom.py index 7773f6f53..99e7c0ca9 100755 --- a/processing/pre/seeds_fromGeom.py +++ b/processing/pre/seeds_fromGeom.py @@ -17,7 +17,7 @@ scriptID = ' '.join([scriptName,damask.version]) #-------------------------------------------------------------------------------------------------- parser = OptionParser(option_class=damask.extendableOption, usage='%prog options [file[s]]', description = """ -Create seed file taking microstructure indices from given geom file. +Create seed file taking material indices from given geom file. Indices can be black-listed or white-listed. """, version = scriptID) @@ -46,12 +46,12 @@ options.blacklist = [int(i) for i in options.blacklist] 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) - microstructure = geom.microstructure.reshape((-1,1),order='F') + geom = damask.Geom.load_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) + materials = geom.materials.reshape((-1,1),order='F') - mask = np.logical_and(np.in1d(microstructure,options.whitelist,invert=False) if options.whitelist else \ + mask = np.logical_and(np.in1d(materials,options.whitelist,invert=False) if options.whitelist else \ np.full(geom.grid.prod(),True,dtype=bool), - np.in1d(microstructure,options.blacklist,invert=True) if options.blacklist else \ + np.in1d(materials,options.blacklist,invert=True) if options.blacklist else \ np.full(geom.grid.prod(),True,dtype=bool)) seeds = damask.grid_filters.cell_coord0(geom.grid,geom.size).reshape(-1,3,order='F') @@ -63,6 +63,6 @@ for name in filenames: 'origin\tx {}\ty {}\tz {}'.format(*geom.origin), 'homogenization\t{}'.format(geom.homogenization)] - table = damask.Table(seeds[mask],{'pos':(3,)},comments) - table = table.add('microstructure',microstructure[mask]) - table.to_file(sys.stdout if name is None else os.path.splitext(name)[0]+'.seeds') + damask.Table(seeds[mask],{'pos':(3,)},comments)\ + .add('material',materials[mask].astype(int))\ + .save(sys.stdout if name is None else os.path.splitext(name)[0]+'.seeds',legacy=True) diff --git a/processing/pre/seeds_fromPokes.py b/processing/pre/seeds_fromPokes.py index 78172fc23..c804cd285 100755 --- a/processing/pre/seeds_fromPokes.py +++ b/processing/pre/seeds_fromPokes.py @@ -52,7 +52,7 @@ options.box = np.array(options.box).reshape(3,2) 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) + geom = damask.Geom.load_ASCII(StringIO(''.join(sys.stdin.read())) if name is None else name) offset =(np.amin(options.box, axis=1)*geom.grid/geom.size).astype(int) box = np.amax(options.box, axis=1) \ @@ -91,6 +91,6 @@ for name in filenames: 'homogenization\t{}'.format(geom.homogenization)] table = damask.Table(seeds,{'pos':(3,),'microstructure':(1,)},comments) - table.set('microstructure',table.get('microstructure').astype(np.int)) - table.to_file(sys.stdout if name is None else \ - os.path.splitext(name)[0]+f'_poked_{options.N}.seeds') + table.set('microstructure',table.get('microstructure').astype(np.int))\ + .save(sys.stdout if name is None else \ + os.path.splitext(name)[0]+f'_poked_{options.N}.seeds',legacy=True) diff --git a/processing/pre/seeds_fromRandom.py b/processing/pre/seeds_fromRandom.py index a544528cf..451e218aa 100755 --- a/processing/pre/seeds_fromRandom.py +++ b/processing/pre/seeds_fromRandom.py @@ -154,12 +154,12 @@ for name in filenames: 'randomSeed\t{}'.format(options.randomSeed), ] - table = damask.Table(np.hstack((seeds,eulers)),{'pos':(3,),'euler':(3,)},comments) - table = table.add('microstructure',np.arange(options.microstructure,options.microstructure + options.N,dtype=int)) + table = damask.Table(np.hstack((seeds,eulers)),{'pos':(3,),'euler':(3,)},comments)\ + .add('microstructure',np.arange(options.microstructure,options.microstructure + options.N,dtype=int)) if options.weights: weights = np.random.uniform(low = 0, high = options.max, size = options.N) if options.max > 0.0 \ else np.random.normal(loc = options.mean, scale = options.sigma, size = options.N) table = table.add('weight',weights) - table.to_file(sys.stdout if name is None else name) + table.save(sys.stdout if name is None else name,legacy=True) diff --git a/python/damask/__init__.py b/python/damask/__init__.py index 1404e88d1..3f2ff6813 100644 --- a/python/damask/__init__.py +++ b/python/damask/__init__.py @@ -18,6 +18,7 @@ from ._lattice import Symmetry, Lattice# noqa from ._orientation import Orientation # noqa from ._result import Result # noqa from ._geom import Geom # noqa +from ._material import Material # noqa from . import solver # noqa # deprecated diff --git a/python/damask/_colormap.py b/python/damask/_colormap.py index fccb8642b..fa7d36ec2 100644 --- a/python/damask/_colormap.py +++ b/python/damask/_colormap.py @@ -235,100 +235,128 @@ class Colormap(mpl.colors.ListedColormap): return Colormap(np.array(rev.colors),rev.name[:-4] if rev.name.endswith('_r_r') else rev.name) - def to_file(self,fname=None,format='ParaView'): + + def save_paraview(self,fname=None): """ - Export colormap to file for use in external programs. + Write colormap to JSON file for Paraview. Parameters ---------- fname : file, str, or pathlib.Path, optional. Filename to store results. If not given, the filename will - consist of the name of the colormap and an extension that - depends on the file format. - format : {'ParaView', 'ASCII', 'GOM', 'gmsh'}, optional - File format, defaults to 'ParaView'. Available formats are: - - ParaView: JSON file, extension '.json'. - - ASCII: Plain text file, extension '.txt'. - - GOM: Aramis GOM (DIC), extension '.legend'. - - Gmsh: Gmsh FEM mesh-generator, extension '.msh'. + consist of the name of the colormap and extension '.json'. """ if fname is not None: try: - f = open(fname,'w') + fhandle = open(fname,'w') except TypeError: - f = fname + fhandle = fname else: - f = None + fhandle = None - if format.lower() == 'paraview': - Colormap._export_paraview(self,f) - elif format.lower() == 'ascii': - Colormap._export_ASCII(self,f) - elif format.lower() == 'gom': - Colormap._export_GOM(self,f) - elif format.lower() == 'gmsh': - Colormap._export_gmsh(self,f) - else: - raise ValueError('Unknown output format: {format}.') - - @staticmethod - def _export_paraview(colormap,fhandle=None): - """Write colormap to JSON file for Paraview.""" colors = [] - for i,c in enumerate(np.round(colormap.colors,6).tolist()): + for i,c in enumerate(np.round(self.colors,6).tolist()): colors+=[i]+c out = [{ 'Creator':util.execution_stamp('Colormap'), 'ColorSpace':'RGB', - 'Name':colormap.name, + 'Name':self.name, 'DefaultMap':True, 'RGBPoints':colors }] if fhandle is None: - with open(colormap.name.replace(' ','_')+'.json', 'w') as f: + with open(self.name.replace(' ','_')+'.json', 'w') as f: json.dump(out, f,indent=4) else: json.dump(out,fhandle,indent=4) - @staticmethod - def _export_ASCII(colormap,fhandle=None): - """Write colormap to ASCII table.""" - labels = {'RGBA':4} if colormap.colors.shape[1] == 4 else {'RGB': 3} - t = Table(colormap.colors,labels,f'Creator: {util.execution_stamp("Colormap")}') + + def save_ASCII(self,fname=None): + """ + Write colormap to ASCII table. + + Parameters + ---------- + fname : file, str, or pathlib.Path, optional. + Filename to store results. If not given, the filename will + consist of the name of the colormap and extension '.txt'. + + """ + if fname is not None: + try: + fhandle = open(fname,'w') + except TypeError: + fhandle = fname + else: + fhandle = None + + labels = {'RGBA':4} if self.colors.shape[1] == 4 else {'RGB': 3} + t = Table(self.colors,labels,f'Creator: {util.execution_stamp("Colormap")}') if fhandle is None: - with open(colormap.name.replace(' ','_')+'.txt', 'w') as f: - t.to_file(f,new_style=True) + with open(self.name.replace(' ','_')+'.txt', 'w') as f: + t.save(f) else: - t.to_file(fhandle,new_style=True) + t.save(fhandle) - @staticmethod - def _export_GOM(colormap,fhandle=None): - """Write colormap to GOM Aramis compatible format.""" + + def save_GOM(self,fname=None): + """ + Write colormap to GOM Aramis compatible format. + + Parameters + ---------- + fname : file, str, or pathlib.Path, optional. + Filename to store results. If not given, the filename will + consist of the name of the colormap and extension '.legend'. + + """ + if fname is not None: + try: + fhandle = open(fname,'w') + except TypeError: + fhandle = fname + else: + fhandle = None # ToDo: test in GOM - GOM_str = f'1 1 {colormap.name.replace(" ","_")} 9 {colormap.name.replace(" ","_")} ' \ + GOM_str = '1 1 {name} 9 {name} '.format(name=self.name.replace(" ","_")) \ + '0 1 0 3 0 0 -1 9 \\ 0 0 0 255 255 255 0 0 255 ' \ - + f'30 NO_UNIT 1 1 64 64 64 255 1 0 0 0 0 0 0 3 0 {len(colormap.colors)}' \ - + ' '.join([f' 0 {c[0]} {c[1]} {c[2]} 255 1' for c in reversed((colormap.colors*255).astype(int))]) \ + + f'30 NO_UNIT 1 1 64 64 64 255 1 0 0 0 0 0 0 3 0 {len(self.colors)}' \ + + ' '.join([f' 0 {c[0]} {c[1]} {c[2]} 255 1' for c in reversed((self.colors*255).astype(int))]) \ + '\n' if fhandle is None: - with open(colormap.name.replace(' ','_')+'.legend', 'w') as f: + with open(self.name.replace(' ','_')+'.legend', 'w') as f: f.write(GOM_str) else: fhandle.write(GOM_str) - @staticmethod - def _export_gmsh(colormap,fhandle=None): - """Write colormap to Gmsh compatible format.""" + def save_gmsh(self,fname=None): + """ + Write colormap to Gmsh compatible format. + + Parameters + ---------- + fname : file, str, or pathlib.Path, optional. + Filename to store results. If not given, the filename will + consist of the name of the colormap and extension '.msh'. + + """ + if fname is not None: + try: + fhandle = open(fname,'w') + except TypeError: + fhandle = fname + else: + fhandle = None # ToDo: test in gmsh gmsh_str = 'View.ColorTable = {\n' \ - +'\n'.join([f'{c[0]},{c[1]},{c[2]},' for c in colormap.colors[:,:3]*255]) \ + +'\n'.join([f'{c[0]},{c[1]},{c[2]},' for c in self.colors[:,:3]*255]) \ +'\n}\n' if fhandle is None: - with open(colormap.name.replace(' ','_')+'.msh', 'w') as f: + with open(self.name.replace(' ','_')+'.msh', 'w') as f: f.write(gmsh_str) else: fhandle.write(gmsh_str) diff --git a/python/damask/_geom.py b/python/damask/_geom.py index b7a7928ca..86d11b9be 100644 --- a/python/damask/_geom.py +++ b/python/damask/_geom.py @@ -1,7 +1,5 @@ -import sys import copy -import multiprocessing -from io import StringIO +import multiprocessing as mp from functools import partial import numpy as np @@ -60,11 +58,11 @@ class Geom: def __repr__(self): """Basic information on geometry definition.""" return util.srepr([ - f'grid a b c: {util.srepr(self.grid, " x ")}', - f'size x y z: {util.srepr(self.size, " x ")}', - f'origin x y z: {util.srepr(self.origin," ")}', - f'# materialpoints: {self.N_materials}', - f'max materialpoint: {np.nanmax(self.materials)}', + f'grid a b c: {util.srepr(self.grid, " x ")}', + f'size x y z: {util.srepr(self.size, " x ")}', + f'origin x y z: {util.srepr(self.origin," ")}', + f'# materials: {self.N_materials}', + f'max material: {np.nanmax(self.materials)}', ]) @@ -102,12 +100,12 @@ class Geom: message.append(util.emph( f'origin x y z: {util.srepr( self.origin," ")}')) if other.N_materials != self.N_materials: - message.append(util.delete(f'# materialpoints: {other.N_materials}')) - message.append(util.emph( f'# materialpoints: { self.N_materials}')) + message.append(util.delete(f'# materials: {other.N_materials}')) + message.append(util.emph( f'# materials: { self.N_materials}')) if np.nanmax(other.materials) != np.nanmax(self.materials): - message.append(util.delete(f'max materialpoint: {np.nanmax(other.materials)}')) - message.append(util.emph( f'max materialpoint: {np.nanmax( self.materials)}')) + message.append(util.delete(f'max material: {np.nanmax(other.materials)}')) + message.append(util.emph( f'max material: {np.nanmax( self.materials)}')) return util.return_message(message) @@ -123,7 +121,7 @@ class Geom: @staticmethod - def from_file(fname): + def load_ASCII(fname): """ Read a geom file. @@ -187,7 +185,7 @@ class Geom: @staticmethod - def from_vtr(fname): + def load(fname): """ Read a VTK rectilinear grid. @@ -198,13 +196,13 @@ class Geom: Valid extension is .vtr, it will be appended if not given. """ - v = VTK.from_file(fname if str(fname).endswith('.vtr') else str(fname)+'.vtr') + v = VTK.load(fname if str(fname).endswith('.vtr') else str(fname)+'.vtr') comments = v.get_comments() grid = np.array(v.vtk_data.GetDimensions())-1 bbox = np.array(v.vtk_data.GetBounds()).reshape(3,2).T size = bbox[1] - bbox[0] - return Geom(v.get('materialpoint').reshape(grid,order='F'),size,bbox[0],comments=comments) + return Geom(v.get('material').reshape(grid,order='F'),size,bbox[0],comments=comments) @staticmethod @@ -241,7 +239,7 @@ class Geom: seeds_p = seeds coords = grid_filters.cell_coord0(grid,size).reshape(-1,3) - pool = multiprocessing.Pool(processes = int(environment.options['DAMASK_NUM_THREADS'])) + pool = mp.Pool(processes = int(environment.options['DAMASK_NUM_THREADS'])) result = pool.map_async(partial(Geom._find_closest_seed,seeds_p,weights_p), [coord for coord in coords]) pool.close() pool.join() @@ -286,131 +284,102 @@ class Geom: ) - def to_file(self,fname,format='vtr',pack=None): + def save_ASCII(self,fname,compress=None): """ Writes a geom file. Parameters ---------- fname : str or file handle - Geometry file to write. - format : {'vtr', 'ASCII'}, optional - File format, defaults to 'vtr'. Available formats are: - - vtr: VTK rectilinear grid file, extension '.vtr'. - - ASCII: Plain text file, extension '.geom'. - pack : bool, optional - Compress ASCII geometry with 'x of y' and 'a to b'. + Geometry file to write with extension '.geom'. + compress : bool, optional + Compress geometry with 'x of y' and 'a to b'. """ - def _to_ASCII(geom,fname,pack=None): - """ - Writes a geom file. + header = [f'{len(self.comments)+4} header'] + self.comments \ + + ['grid a {} b {} c {}'.format(*self.grid), + 'size x {} y {} z {}'.format(*self.size), + 'origin x {} y {} z {}'.format(*self.origin), + 'homogenization 1', + ] - Parameters - ---------- - geom : Geom object - Geometry to write. - fname : str or file handle - Geometry file to write. - pack : bool, optional - Compress geometry with 'x of y' and 'a to b'. + grid = self.grid - """ - header = [f'{len(geom.comments)+4} header'] + geom.comments \ - +[ 'grid a {} b {} c {}'.format(*geom.grid), - 'size x {} y {} z {}'.format(*geom.size), - 'origin x {} y {} z {}'.format(*geom.origin), - 'homogenization 1', - ] - - grid = geom.grid - - if pack is None: - plain = grid.prod()/geom.N_materials < 250 - else: - plain = not pack - - if plain: - format_string = '%g' if geom.materials.dtype in np.sctypes['float'] else \ - '%{}i'.format(1+int(np.floor(np.log10(np.nanmax(geom.materials))))) - np.savetxt(fname, - geom.materials.reshape([grid[0],np.prod(grid[1:])],order='F').T, - header='\n'.join(header), fmt=format_string, comments='') - else: - try: - f = open(fname,'w') - except TypeError: - f = fname - - compressType = None - former = start = -1 - reps = 0 - for current in geom.materials.flatten('F'): - if abs(current - former) == 1 and (start - current) == reps*(former - current): - compressType = 'to' - reps += 1 - elif current == former and start == former: - compressType = 'of' - reps += 1 - else: - if compressType is None: - f.write('\n'.join(header)+'\n') - elif compressType == '.': - f.write(f'{former}\n') - elif compressType == 'to': - f.write(f'{start} to {former}\n') - elif compressType == 'of': - f.write(f'{reps} of {former}\n') - - compressType = '.' - start = current - reps = 1 - - former = current - - if compressType == '.': - f.write(f'{former}\n') - elif compressType == 'to': - f.write(f'{start} to {former}\n') - elif compressType == 'of': - f.write(f'{reps} of {former}\n') - - - def _to_vtr(geom,fname=None): - """ - Generates vtk rectilinear grid. - - Parameters - ---------- - geom : Geom object - Geometry to write. - fname : str, optional - Filename to write. If no file is given, a string is returned. - Valid extension is .vtr, it will be appended if not given. - - """ - v = VTK.from_rectilinearGrid(geom.grid,geom.size,geom.origin) - v.add(geom.materials.flatten(order='F'),'materialpoint') - v.add_comments(geom.comments) - - if fname: - v.to_file(fname if str(fname).endswith('.vtr') else str(fname)+'.vtr') - else: - sys.stdout.write(v.__repr__()) - - if format.lower() == 'ascii': - return _to_ASCII(self,fname,pack) - elif format.lower() == 'vtr': - return _to_vtr(self,fname) + if compress is None: + plain = grid.prod()/self.N_materials < 250 else: - raise TypeError(f'Unknown format {format}.') + plain = not compress - def as_ASCII(self,pack=False): - """Format geometry as human-readable ASCII.""" - f = StringIO() - self.to_file(f,'ASCII',pack) - f.seek(0) - return ''.join(f.readlines()) + if plain: + format_string = '%g' if self.materials.dtype in np.sctypes['float'] else \ + '%{}i'.format(1+int(np.floor(np.log10(np.nanmax(self.materials))))) + np.savetxt(fname, + self.materials.reshape([grid[0],np.prod(grid[1:])],order='F').T, + header='\n'.join(header), fmt=format_string, comments='') + else: + try: + f = open(fname,'w') + except TypeError: + f = fname + + compressType = None + former = start = -1 + reps = 0 + for current in self.materials.flatten('F'): + if abs(current - former) == 1 and (start - current) == reps*(former - current): + compressType = 'to' + reps += 1 + elif current == former and start == former: + compressType = 'of' + reps += 1 + else: + if compressType is None: + f.write('\n'.join(header)+'\n') + elif compressType == '.': + f.write(f'{former}\n') + elif compressType == 'to': + f.write(f'{start} to {former}\n') + elif compressType == 'of': + f.write(f'{reps} of {former}\n') + + compressType = '.' + start = current + reps = 1 + + former = current + + if compressType == '.': + f.write(f'{former}\n') + elif compressType == 'to': + f.write(f'{start} to {former}\n') + elif compressType == 'of': + f.write(f'{reps} of {former}\n') + + + def save(self,fname,compress=True): + """ + Generates vtk rectilinear grid. + + Parameters + ---------- + fname : str, optional + Filename to write. If no file is given, a string is returned. + Valid extension is .vtr, it will be appended if not given. + compress : bool, optional + Compress with zlib algorithm. Defaults to True. + + """ + v = VTK.from_rectilinearGrid(self.grid,self.size,self.origin) + v.add(self.materials.flatten(order='F'),'material') + v.add_comments(self.comments) + + v.save(fname if str(fname).endswith('.vtr') else str(fname)+'.vtr',parallel=False,compress=compress) + + + def show(self): + """Show on screen.""" + v = VTK.from_rectilinearGrid(self.grid,self.size,self.origin) + v.show() def add_primitive(self,dimension,center,exponent, diff --git a/python/damask/_material.py b/python/damask/_material.py new file mode 100644 index 000000000..106d1582e --- /dev/null +++ b/python/damask/_material.py @@ -0,0 +1,193 @@ +from io import StringIO +import copy + +import yaml +import numpy as np + +from . import Lattice +from . import Rotation + +class NiceDumper(yaml.SafeDumper): + """Make YAML readable for humans.""" + + def write_line_break(self, data=None): + super().write_line_break(data) + + if len(self.indents) == 1: + super().write_line_break() + + def increase_indent(self, flow=False, indentless=False): + return super().increase_indent(flow, False) + + +class Material(dict): + """Material configuration.""" + + def __repr__(self): + """Show as in file.""" + output = StringIO() + self.save(output) + output.seek(0) + return ''.join(output.readlines()) + + @staticmethod + def load(fname): + """Load from yaml file.""" + try: + fhandle = open(fname) + except TypeError: + fhandle = fname + return Material(yaml.safe_load(fhandle)) + + def save(self,fname='material.yaml'): + """ + Save to yaml file. + + Parameters + ---------- + fname : file, str, or pathlib.Path + Filename or file for reading. + + """ + try: + fhandle = open(fname,'w') + except TypeError: + fhandle = fname + fhandle.write(yaml.dump(dict(self),width=256,default_flow_style=None,Dumper=NiceDumper)) + + + @property + def is_complete(self): + """Check for completeness.""" + ok = True + for top_level in ['homogenization','phase','microstructure']: + # ToDo: With python 3.8 as prerequisite we can shorten with := + ok &= top_level in self + if top_level not in self: print(f'{top_level} entry missing') + + if ok: + ok &= len(self['microstructure']) > 0 + if len(self['microstructure']) < 1: print('Incomplete microstructure definition') + + if ok: + homogenization = set() + phase = set() + for i,v in enumerate(self['microstructure']): + if 'homogenization' in v: + homogenization.add(v['homogenization']) + else: + print(f'No homogenization specified in microstructure {i}') + ok = False + + if 'constituents' in v: + for ii,vv in enumerate(v['constituents']): + if 'orientation' not in vv: + print('No orientation specified in constituent {ii} of microstructure {i}') + ok = False + if 'phase' in vv: + phase.add(vv['phase']) + else: + print(f'No phase specified in constituent {ii} of microstructure {i}') + ok = False + + for k,v in self['phase'].items(): + if 'lattice' not in v: + print(f'No lattice specified in phase {k}') + ok = False + + #for k,v in self['homogenization'].items(): + # if 'N_constituents' not in v: + # print(f'No. of constituents not specified in homogenization {k}'} + # ok = False + + if phase - set(self['phase']): + print(f'Phase(s) {phase-set(self["phase"])} missing') + ok = False + if homogenization - set(self['homogenization']): + print(f'Homogenization(s) {homogenization-set(self["homogenization"])} missing') + ok = False + + return ok + + + @property + def is_valid(self): + """Check for valid file layout.""" + ok = True + + if 'phase' in self: + for k,v in self['phase'].items(): + if 'lattice' in v: + try: + Lattice(v['lattice']) + except KeyError: + s = v['lattice'] + print(f"Invalid lattice: '{s}' in phase '{k}'") + ok = False + + if 'microstructure' in self: + for i,v in enumerate(self['microstructure']): + if 'constituents' in v: + f = 0.0 + for c in v['constituents']: + f+= float(c['fraction']) + if 'orientation' in c: + try: + Rotation.from_quaternion(c['orientation']) + except ValueError: + o = c['orientation'] + print(f"Invalid orientation: '{o}' in microstructure '{i}'") + ok = False + if not np.isclose(f,1.0): + print(f"Invalid total fraction '{f}' in microstructure '{i}'") + ok = False + + return ok + + + def microstructure_rename_phase(self,mapping,ID=None,constituent=None): + """ + Change phase name in microstructure. + + Parameters + ---------- + mapping: dictionary + Mapping from old name to new name + ID: list of ints, optional + Limit renaming to selected microstructure IDs. + constituent: list of ints, optional + Limit renaming to selected constituents. + + """ + dup = copy.deepcopy(self) + for i,m in enumerate(dup['microstructure']): + if ID and i not in ID: continue + for c in m['constituents']: + if constituent is not None and c not in constituent: continue + try: + c['phase'] = mapping[c['phase']] + except KeyError: + continue + return dup + + + def microstructure_rename_homogenization(self,mapping,ID=None): + """ + Change homogenization name in microstructure. + + Parameters + ---------- + mapping: dictionary + Mapping from old name to new name + ID: list of ints, optional + Limit renaming to selected homogenization IDs. + + """ + dup = copy.deepcopy(self) + for i,m in enumerate(dup['microstructure']): + if ID and i not in ID: continue + try: + m['homogenization'] = mapping[m['homogenization']] + except KeyError: + continue + return dup diff --git a/python/damask/_result.py b/python/damask/_result.py index 723a9f3eb..5e8a9a9d0 100644 --- a/python/damask/_result.py +++ b/python/damask/_result.py @@ -1100,7 +1100,7 @@ class Result: pool.join() - def write_XDMF(self): + def save_XDMF(self): """ Write XDMF file to directly visualize data in DADF5 file. @@ -1196,7 +1196,7 @@ class Result: f.write(xml.dom.minidom.parseString(ET.tostring(xdmf).decode()).toprettyxml()) - def to_vtk(self,labels=[],mode='cell'): + def save_vtk(self,labels=[],mode='cell'): """ Export to vtk cell/point data. @@ -1268,4 +1268,4 @@ class Result: u = self.read_dataset(self.get_dataset_location('u_n' if mode.lower() == 'cell' else 'u_p')) v.add(u,'u') - v.to_file(f'{self.fname.stem}_inc{inc[3:].zfill(N_digits)}') + v.save(f'{self.fname.stem}_inc{inc[3:].zfill(N_digits)}') diff --git a/python/damask/_table.py b/python/damask/_table.py index b4fd2975a..431cf1886 100644 --- a/python/damask/_table.py +++ b/python/damask/_table.py @@ -27,8 +27,11 @@ class Table: self.comments = [] if comments_ is None else [c for c in comments_] self.data = pd.DataFrame(data=data) self.shapes = { k:(v,) if isinstance(v,(np.int,int)) else v for k,v in shapes.items() } - self._label_condensed() + self._label_uniform() + def __repr__(self): + """Brief overview.""" + return util.srepr(self.comments)+'\n'+self.data.__repr__() def __copy__(self): """Copy Table.""" @@ -39,7 +42,7 @@ class Table: return self.__copy__() - def _label_flat(self): + def _label_discrete(self): """Label data individually, e.g. v v v ==> 1_v 2_v 3_v.""" labels = [] for label,shape in self.shapes.items(): @@ -48,8 +51,8 @@ class Table: self.data.columns = labels - def _label_condensed(self): - """Label data condensed, e.g. 1_v 2_v 3_v ==> v v v.""" + def _label_uniform(self): + """Label data uniformly, e.g. 1_v 2_v 3_v ==> v v v.""" labels = [] for label,shape in self.shapes.items(): labels += [label] * int(np.prod(shape)) @@ -64,12 +67,15 @@ class Table: @staticmethod - def from_ASCII(fname): + def load(fname): """ - Create table from ASCII file. + Load ASCII table file. - The first line can indicate the number of subsequent header lines as 'n header', - alternatively first line is the header and comments are marked by '#' ('new style'). + In legacy style, the first line indicates the number of + subsequent header lines as "N header", with the last header line being + interpreted as column labels. + Alternatively, initial comments are marked by '#', with the first non-comment line + containing the column labels. Vector data column labels are indicated by '1_v, 2_v, ..., n_v'. Tensor data column labels are indicated by '3x3:1_T, 3x3:2_T, ..., 3x3:9_T'. @@ -119,9 +125,9 @@ class Table: return Table(data,shapes,comments) @staticmethod - def from_ang(fname): + def load_ang(fname): """ - Create table from TSL ang file. + Load ang file. A valid TSL ang file needs to contains the following columns: * Euler angles (Bunge notation) in radians, 3 floats, label 'eu'. @@ -289,9 +295,9 @@ class Table: """ dup = self.copy() - dup._label_flat() + dup._label_discrete() dup.data.sort_values(labels,axis=0,inplace=True,ascending=ascending) - dup._label_condensed() + dup._label_uniform() dup.comments.append(f'sorted {"ascending" if ascending else "descending"} by {labels}') return dup @@ -338,59 +344,38 @@ class Table: return dup - def to_file(self,fname,format='ASCII',new_style=False): + def save(self,fname,legacy=False): """ - Store as plain text file. + Save as plain text file. Parameters ---------- fname : file, str, or pathlib.Path Filename or file for writing. - format : {ASCII'}, optional - File format, defaults to 'ASCII'. Available formats are: - - ASCII: Plain text file, extension '.txt'. - new_style : Boolean, optional - Write table in new style, indicating header lines by comment sign ('#') only. + legacy : Boolean, optional + Write table in legacy style, indicating header lines by "N header" + in contrast to using comment sign ('#') at beginning of lines. """ - def _to_ASCII(table,fname,new_style=False): - """ - Store as plain text file. + seen = set() + labels = [] + for l in [x for x in self.data.columns if not (x in seen or seen.add(x))]: + if self.shapes[l] == (1,): + labels.append(f'{l}') + elif len(self.shapes[l]) == 1: + labels += [f'{i+1}_{l}' \ + for i in range(self.shapes[l][0])] + else: + labels += [f'{util.srepr(self.shapes[l],"x")}:{i+1}_{l}' \ + for i in range(np.prod(self.shapes[l]))] - Parameters - ---------- - table : Table object - Table to write. - fname : file, str, or pathlib.Path - Filename or file for writing. - new_style : Boolean, optional - Write table in new style, indicating header lines by comment sign ('#') only. + header = ([f'{len(self.comments)+1} header'] + self.comments) if legacy else \ + [f'# {comment}' for comment in self.comments] - """ - seen = set() - labels = [] - for l in [x for x in table.data.columns if not (x in seen or seen.add(x))]: - if table.shapes[l] == (1,): - labels.append(f'{l}') - elif len(table.shapes[l]) == 1: - labels += [f'{i+1}_{l}' \ - for i in range(table.shapes[l][0])] - else: - labels += [f'{util.srepr(table.shapes[l],"x")}:{i+1}_{l}' \ - for i in range(np.prod(table.shapes[l]))] + try: + fhandle = open(fname,'w') + except TypeError: + fhandle = fname - header = [f'# {comment}' for comment in table.comments] if new_style else \ - [f'{len(table.comments)+1} header'] + table.comments - - try: - f = open(fname,'w') - except TypeError: - f = fname - - for line in header + [' '.join(labels)]: f.write(line+'\n') - table.data.to_csv(f,sep=' ',na_rep='nan',index=False,header=False) - - if format.lower() == 'ascii': - return _to_ASCII(self,fname,new_style) - else: - raise TypeError(f'Unknown format {format}.') + for line in header + [' '.join(labels)]: fhandle.write(line+'\n') + self.data.to_csv(fhandle,sep=' ',na_rep='nan',index=False,header=False) diff --git a/python/damask/_test.py b/python/damask/_test.py index 78616fabf..5cadc9dfe 100644 --- a/python/damask/_test.py +++ b/python/damask/_test.py @@ -228,7 +228,7 @@ class Test: def copy_Base2Current(self,sourceDir,sourcefiles=[],targetfiles=[]): - source=os.path.normpath(os.path.join(self.dirBase,'../../..',sourceDir)) + source = os.path.normpath(os.path.join(self.dirBase,'../../..',sourceDir)) if len(targetfiles) == 0: targetfiles = sourcefiles for i,f in enumerate(sourcefiles): try: @@ -287,30 +287,30 @@ class Test: import numpy as np logging.info('\n '.join(['comparing',File1,File2])) - table = damask.Table.from_ASCII(File1) - len1=len(table.comments)+2 - table = damask.Table.from_ASCII(File2) - len2=len(table.comments)+2 + table = damask.Table.load(File1) + len1 = len(table.comments)+2 + table = damask.Table.load(File2) + len2 = len(table.comments)+2 refArray = np.nan_to_num(np.genfromtxt(File1,missing_values='n/a',skip_header = len1,autostrip=True)) curArray = np.nan_to_num(np.genfromtxt(File2,missing_values='n/a',skip_header = len2,autostrip=True)) if len(curArray) == len(refArray): refArrayNonZero = refArray[refArray.nonzero()] - curArray = curArray[refArray.nonzero()] - max_err=np.max(abs(refArrayNonZero[curArray.nonzero()]/curArray[curArray.nonzero()]-1.)) - max_loc=np.argmax(abs(refArrayNonZero[curArray.nonzero()]/curArray[curArray.nonzero()]-1.)) + curArray = curArray[refArray.nonzero()] + max_err = np. max(abs(refArrayNonZero[curArray.nonzero()]/curArray[curArray.nonzero()]-1.)) + max_loc = np.argmax(abs(refArrayNonZero[curArray.nonzero()]/curArray[curArray.nonzero()]-1.)) refArrayNonZero = refArrayNonZero[curArray.nonzero()] - curArray = curArray[curArray.nonzero()] + curArray = curArray[curArray.nonzero()] print(f' ********\n * maximum relative error {max_err} between {refArrayNonZero[max_loc]} and {curArray[max_loc]}\n ********') return max_err else: - raise Exception('mismatch in array size to compare') + raise Exception(f'mismatch in array sizes ({len(refArray)} and {len(curArray)}) to compare') def compare_ArrayRefCur(self,ref,cur=''): - if cur =='': cur = ref + if cur == '': cur = ref refName = self.fileInReference(ref) curName = self.fileInCurrent(cur) return self.compare_Array(refName,curName) @@ -331,7 +331,7 @@ class Test: logging.info('\n '.join(['comparing ASCII Tables',file0,file1])) if normHeadings == '': normHeadings = headings0 -# check if comparison is possible and determine lenght of columns +# check if comparison is possible and determine length of columns if len(headings0) == len(headings1) == len(normHeadings): dataLength = len(headings0) length = [1 for i in range(dataLength)] @@ -399,10 +399,8 @@ class Test: if any(norm[i]) == 0.0 or absTol[i]: norm[i] = [1.0 for j in range(line0-len(skipLines))] absTol[i] = True - if perLine: - logging.warning(f"At least one norm of \"{headings0[i]['label']}\" in first table is 0.0, using absolute tolerance") - else: - logging.warning(f"Maximum norm of \"{headings0[i]['label']}\" in first table is 0.0, using absolute tolerance") + logging.warning(f'''{"At least one" if perLine else "Maximum"} norm of + "{headings0[i]['label']}" in first table is 0.0, using absolute tolerance''') line1 = 0 while table1.data_read(): # read next data line of ASCII table @@ -418,20 +416,18 @@ class Test: logging.info(' ********') for i in range(dataLength): - if absTol[i]: - logging.info(f" * maximum absolute error {maxError[i]} between {headings0[i]['label']} and {headings1[i]['label']}") - else: - logging.info(f" * maximum relative error {maxError[i]} between {headings0[i]['label']} and {headings1[i]['label']}") + logging.info(f''' * maximum {'absolute' if absTol[i] else 'relative'} error {maxError[i]} + between {headings0[i]['label']} and {headings1[i]['label']}''') logging.info(' ********') return maxError def compare_TablesStatistically(self, - files = [None,None], # list of file names - columns = [None], # list of list of column labels (per file) - meanTol = 1.0e-4, - stdTol = 1.0e-6, - preFilter = 1.0e-9): + files = [None,None], # list of file names + columns = [None], # list of list of column labels (per file) + meanTol = 1.0e-4, + stdTol = 1.0e-6, + preFilter = 1.0e-9): """ Calculate statistics of tables. @@ -440,9 +436,9 @@ class Test: if not (isinstance(files, Iterable) and not isinstance(files, str)): # check whether list of files is requested files = [str(files)] - tables = [damask.Table.from_ASCII(filename) for filename in files] + tables = [damask.Table.load(filename) for filename in files] for table in tables: - table._label_flat() + table._label_discrete() columns += [columns[0]]*(len(files)-len(columns)) # extend to same length as files columns = columns[:len(files)] # truncate to same length as files @@ -462,7 +458,7 @@ class Test: data = [] for table,labels in zip(tables,columns): - table._label_condensed() + table._label_uniform() data.append(np.hstack(list(table.get(label) for label in labels))) @@ -471,12 +467,11 @@ class Test: normBy = (np.abs(data[i]) + np.abs(data[i-1]))*0.5 normedDelta = np.where(normBy>preFilter,delta/normBy,0.0) mean = np.amax(np.abs(np.mean(normedDelta,0))) - std = np.amax(np.std(normedDelta,0)) + std = np.amax(np.std(normedDelta,0)) logging.info(f'mean: {mean:f}') logging.info(f'std: {std:f}') - return (mean + + + AQAAAACAAABJAAAATgAAAA==eF4FwUEKgCAUBNCO4rIWX8ZJsbxA5/iUFqQVBJ2/9zZt+p52yXeza816mW+0sBCtz6HCGGSPE1wJjMX0BCGYhTQuJLrkKfDA0P0d3xK6 + + - + AQAAAACAAAAABQAAZwAAAA==eF7t0rcOgmAAhVEgNmyo2AuoWN//BR04EwsJcfzvcvabL47qxcFOJg177HPAIUdMOeaEU844Z8YFl1wx55obbrnjngceeeKZFxYseeWNd1Z88MkX3/zwy+Z/wf8YOqzX1uEPlgwHCA== diff --git a/python/tests/reference/Geom/clean_2_1+2+3_True.vtr b/python/tests/reference/Geom/clean_2_1+2+3_True.vtr index eb2d1b64a..eafbde5a4 100644 --- a/python/tests/reference/Geom/clean_2_1+2+3_True.vtr +++ b/python/tests/reference/Geom/clean_2_1+2+3_True.vtr @@ -1,11 +1,16 @@ + + + AQAAAACAAABJAAAATwAAAA==eF5LScxNLM7Wc0/Nz9VLzklNzFMoM9Yz0DPQTcwpyEjUNTI31U03tzAwTDM1Mk9T0DAyMDLQNbDUNTJSMDS1MjK0MgFyTQwMNBkAHc8SuA== + + - + AQAAAACAAAAABQAAagAAAA==eF7t0rkOglAARFExLrgCKuKuqLj8/w9acCoSY7B+05x+cqNOvSj4l92GPfY54JAxRxxzwilnnDNhyowLLrlizjULbrjljnseeOSJZ15Y8sob76z44JMvvtn8L9jObz2GDuv96vADk5QHBg== diff --git a/python/tests/reference/Geom/clean_2_1_False.vtr b/python/tests/reference/Geom/clean_2_1_False.vtr index e77743025..c04d48be6 100644 --- a/python/tests/reference/Geom/clean_2_1_False.vtr +++ b/python/tests/reference/Geom/clean_2_1_False.vtr @@ -1,11 +1,16 @@ + + + AQAAAACAAABJAAAATwAAAA==eF4FwdEJgDAMBUBH6ad+JLzElmoXcI6grYKtCoLze7dZs/fkJd+N15rtct/IYJDV5zDSGGiPE6QEjcX1CgVhJlUnIakkLwQPDN0PHdcSuQ== + + - + AQAAAACAAAAABQAAZAAAAA==eF7t0scRglAAQEEBAyZUMCuomPtv0ANbgMNw/O+yDbyo1xQFWxkzYZ8DDjliyjEnnHLGOTMuuOSKOQuuueGWO+554JEnnlmy4oVX3ljzzgeffPHND7+Mg50aPmz698MfmvQHCg== diff --git a/python/tests/reference/Geom/clean_2_1_True.vtr b/python/tests/reference/Geom/clean_2_1_True.vtr index d8a3d4169..19c277c7f 100644 --- a/python/tests/reference/Geom/clean_2_1_True.vtr +++ b/python/tests/reference/Geom/clean_2_1_True.vtr @@ -1,11 +1,16 @@ + + + AQAAAACAAABJAAAATwAAAA==eF4FwVEKgCAQBcCO4md97PJcE9MLdI6ltCCtIOj8zuza9Lt4zU/jrWa9ze8YDNL6nkoSPB1hgS1eQjGjQECIJGKsT2KTi4QZmIYOHg4SwA== + + - + AQAAAACAAAAABQAAYwAAAA==eF7t0scBgkAAAEHBgBEwgDmBsf8GfTANCN/bzzSwUa8pCrYyZp8DDjliwjEnnHLGORdMmTHnkiuuuWHBklvuuOeBR5545oVX3nhnxZoPPvnimx9+GQc7GT5sqvjvhz+ZtAcJ diff --git a/python/tests/reference/Geom/clean_2_None_False.vtr b/python/tests/reference/Geom/clean_2_None_False.vtr index 068186206..58e8cb8fb 100644 --- a/python/tests/reference/Geom/clean_2_None_False.vtr +++ b/python/tests/reference/Geom/clean_2_None_False.vtr @@ -1,11 +1,16 @@ + + + AQAAAACAAABJAAAATwAAAA==eF4FwdEJgDAMBUBH6ad+JLzElmoXcI6grYKtCoLze7dZs/fkJd+N15rtct/IYJDV5zDSGGiPE6QEjcX1CgVhJlUnIakkLwQPDN0PHdcSuQ== + + - + AQAAAACAAAAABQAAGwAAAA==eF5jZIAAxlF6lB4AmmmUpogeDUfKaAD7jwDw diff --git a/python/tests/reference/Geom/clean_2_None_True.vtr b/python/tests/reference/Geom/clean_2_None_True.vtr index e72fa97e1..37bb3b08b 100644 --- a/python/tests/reference/Geom/clean_2_None_True.vtr +++ b/python/tests/reference/Geom/clean_2_None_True.vtr @@ -1,11 +1,16 @@ + + + AQAAAACAAABJAAAATwAAAA==eF4FwVEKgCAQBcCO4md97PJcE9MLdI6ltCCtIOj8zuza9Lt4zU/jrWa9ze8YDNL6nkoSPB1hgS1eQjGjQECIJGKsT2KTi4QZmIYOHg4SwA== + + - + AQAAAACAAAAABQAAGQAAAA==eF5jZIAAxlF6lB4AmmmUHqUHkAYA/M8A8Q== diff --git a/python/tests/reference/Geom/clean_3_1+2+3_False.vtr b/python/tests/reference/Geom/clean_3_1+2+3_False.vtr index 5400fcdb6..955c58a2a 100644 --- a/python/tests/reference/Geom/clean_3_1+2+3_False.vtr +++ b/python/tests/reference/Geom/clean_3_1+2+3_False.vtr @@ -1,11 +1,16 @@ + + + AQAAAACAAABJAAAATgAAAA==eF4FwUEKgCAUBNCO4rIWX8ZJsbxA5/iUFqQVBJ2/9zZt+p52yXeza816mW+0sBCtz6HCGGSPE1wJjMX0BCGYhTQuJLrkKfDA0P0d3xK6 + + - + AQAAAACAAAAABQAAZwAAAA==eF7t0rcOgmAAhVEgNmyo2AuoWN//BR04EwsJcfzvcvabL47qxcFOJg177HPAIUdMOeaEU844Z8YFl1wx55obbrnjngceeeKZFxYseeWNd1Z88MkX3/zwy+Z/wf8YOqzX1uEPlgwHCA== diff --git a/python/tests/reference/Geom/clean_3_1+2+3_True.vtr b/python/tests/reference/Geom/clean_3_1+2+3_True.vtr index eb2d1b64a..eafbde5a4 100644 --- a/python/tests/reference/Geom/clean_3_1+2+3_True.vtr +++ b/python/tests/reference/Geom/clean_3_1+2+3_True.vtr @@ -1,11 +1,16 @@ + + + AQAAAACAAABJAAAATwAAAA==eF5LScxNLM7Wc0/Nz9VLzklNzFMoM9Yz0DPQTcwpyEjUNTI31U03tzAwTDM1Mk9T0DAyMDLQNbDUNTJSMDS1MjK0MgFyTQwMNBkAHc8SuA== + + - + AQAAAACAAAAABQAAagAAAA==eF7t0rkOglAARFExLrgCKuKuqLj8/w9acCoSY7B+05x+cqNOvSj4l92GPfY54JAxRxxzwilnnDNhyowLLrlizjULbrjljnseeOSJZ15Y8sob76z44JMvvtn8L9jObz2GDuv96vADk5QHBg== diff --git a/python/tests/reference/Geom/clean_3_1_False.vtr b/python/tests/reference/Geom/clean_3_1_False.vtr index e77743025..c04d48be6 100644 --- a/python/tests/reference/Geom/clean_3_1_False.vtr +++ b/python/tests/reference/Geom/clean_3_1_False.vtr @@ -1,11 +1,16 @@ + + + AQAAAACAAABJAAAATwAAAA==eF4FwdEJgDAMBUBH6ad+JLzElmoXcI6grYKtCoLze7dZs/fkJd+N15rtct/IYJDV5zDSGGiPE6QEjcX1CgVhJlUnIakkLwQPDN0PHdcSuQ== + + - + AQAAAACAAAAABQAAZAAAAA==eF7t0scRglAAQEEBAyZUMCuomPtv0ANbgMNw/O+yDbyo1xQFWxkzYZ8DDjliyjEnnHLGOTMuuOSKOQuuueGWO+554JEnnlmy4oVX3ljzzgeffPHND7+Mg50aPmz698MfmvQHCg== diff --git a/python/tests/reference/Geom/clean_3_1_True.vtr b/python/tests/reference/Geom/clean_3_1_True.vtr index d8a3d4169..2fb4afd9c 100644 --- a/python/tests/reference/Geom/clean_3_1_True.vtr +++ b/python/tests/reference/Geom/clean_3_1_True.vtr @@ -1,11 +1,16 @@ + + + AQAAAACAAABJAAAATwAAAA==eF5LScxNLM7Wc0/Nz9VLzklNzFMoM9Yz0DPQTcwpyEjUNTI31U03tzAwTDM1Mk9T0DAyMDLQNbDUNTJSMDS1MjK0MgFyTQwMNBkAHc8SuA== + + - + AQAAAACAAAAABQAAYwAAAA==eF7t0scBgkAAAEHBgBEwgDmBsf8GfTANCN/bzzSwUa8pCrYyZp8DDjliwjEnnHLGORdMmTHnkiuuuWHBklvuuOeBR5545oVX3nhnxZoPPvnimx9+GQc7GT5sqvjvhz+ZtAcJ diff --git a/python/tests/reference/Geom/clean_3_None_False.vtr b/python/tests/reference/Geom/clean_3_None_False.vtr index f7cd54cc0..d63601aa6 100644 --- a/python/tests/reference/Geom/clean_3_None_False.vtr +++ b/python/tests/reference/Geom/clean_3_None_False.vtr @@ -1,11 +1,16 @@ + + + AQAAAACAAABJAAAATwAAAA==eF4FwdEJgDAMBUBH6ad+JLzElmoXcI6grYKtCoLze7dZs/fkJd+N15rtct/IYJDV5zDSGGiPE6QEjcX1CgVhJlUnIakkLwQPDN0PHdcSuQ== + + - + AQAAAACAAAAABQAAIgAAAA==eF5jZIAAxlGaLJoJjSakntr6hzqN7v9RepSmJw0AC04A9Q== diff --git a/python/tests/reference/Geom/clean_3_None_True.vtr b/python/tests/reference/Geom/clean_3_None_True.vtr index 2ebca1695..ffdb40218 100644 --- a/python/tests/reference/Geom/clean_3_None_True.vtr +++ b/python/tests/reference/Geom/clean_3_None_True.vtr @@ -1,11 +1,16 @@ + + + AQAAAACAAABJAAAATwAAAA==eF4FwVEKgCAQBcCO4md97PJcE9MLdI6ltCCtIOj8zuza9Lt4zU/jrWa9ze8YDNL6nkoSPB1hgS1eQjGjQECIJGKsT2KTi4QZmIYOHg4SwA== + + - + AQAAAACAAAAABQAALwAAAA==eF5jZIAAxlGaLJoJjSakHpc+cvUTUkdrmlL3j9KU0dROF5TqH2iaVPcDAALOANU= diff --git a/python/tests/reference/Geom/clean_4_1+2+3_False.vtr b/python/tests/reference/Geom/clean_4_1+2+3_False.vtr index 20f83428f..dfb12dae0 100644 --- a/python/tests/reference/Geom/clean_4_1+2+3_False.vtr +++ b/python/tests/reference/Geom/clean_4_1+2+3_False.vtr @@ -1,11 +1,16 @@ + + + AQAAAACAAABJAAAATgAAAA==eF4FwUEKgCAUBNCO4rIWX8ZJsbxA5/iUFqQVBJ2/9zZt+p52yXeza816mW+0sBCtz6HCGGSPE1wJjMX0BCGYhTQuJLrkKfDA0P0d3xK6 + + - + AQAAAACAAAAABQAAcQAAAA==eF7t0rkOglAUBFAxKu6igvsKrv//gxYcm9fQGEPBNKe6yc1kolaZqPEndthljzH7HHDIEceccMoZE8654JIpM6645oZb7rjngUeeeOaFV+YseOOdDz754pthf+3Aqr7rdv9vw3+/NjssU7XDD0/8BuQ= diff --git a/python/tests/reference/Geom/clean_4_1+2+3_True.vtr b/python/tests/reference/Geom/clean_4_1+2+3_True.vtr index ae1490bce..5c228bb32 100644 --- a/python/tests/reference/Geom/clean_4_1+2+3_True.vtr +++ b/python/tests/reference/Geom/clean_4_1+2+3_True.vtr @@ -1,11 +1,16 @@ + + + AQAAAACAAABJAAAATwAAAA==eF5LScxNLM7Wc0/Nz9VLzklNzFMoM9Yz0DPQTcwpyEjUNTI31U03tzAwTDM1Mk9T0DAyMDLQNbDUNTJSMDS1MjK0MgFyTQwMNBkAHc8SuA== + + - + AQAAAACAAAAABQAAYQAAAA==eF7t0scVglAAAEHgqZgBA2ZExdR/gx6YCpDj38s0sEnUlgR7ccAhR0w55oRTzjjngktmzFlwxTU33LLkjnseeOSJZ15Y8cqaN975YMMnX3zzwy/j4F+GD9u6fvgD+gwHCA== diff --git a/python/tests/reference/Geom/clean_4_1_False.vtr b/python/tests/reference/Geom/clean_4_1_False.vtr index e77743025..1d4fe29b9 100644 --- a/python/tests/reference/Geom/clean_4_1_False.vtr +++ b/python/tests/reference/Geom/clean_4_1_False.vtr @@ -1,11 +1,16 @@ + + + AQAAAACAAABJAAAATgAAAA==eF4FwUEKgCAUBNCO4rIWX8ZJsbxA5/iUFqQVBJ2/9zZt+p52yXeza816mW+0sBCtz6HCGGSPE1wJjMX0BCGYhTQuJLrkKfDA0P0d3xK6 + + - + AQAAAACAAAAABQAAZAAAAA==eF7t0scRglAAQEEBAyZUMCuomPtv0ANbgMNw/O+yDbyo1xQFWxkzYZ8DDjliyjEnnHLGOTMuuOSKOQuuueGWO+554JEnnlmy4oVX3ljzzgeffPHND7+Mg50aPmz698MfmvQHCg== diff --git a/python/tests/reference/Geom/clean_4_1_True.vtr b/python/tests/reference/Geom/clean_4_1_True.vtr index d042cf7b4..8e8b5b8ef 100644 --- a/python/tests/reference/Geom/clean_4_1_True.vtr +++ b/python/tests/reference/Geom/clean_4_1_True.vtr @@ -1,11 +1,16 @@ + + + AQAAAACAAABJAAAATwAAAA==eF5LScxNLM7Wc0/Nz9VLzklNzFMoM9Yz0DPQTcwpyEjUNTI31U03tzAwTDM1Mk9T0DAyMDLQNbDUNTJSMDS1MjK0MgFyTQwMNBkAHc8SuA== + + - + AQAAAACAAAAABQAAZAAAAA==eF7t0rcSglAARFEHE0bAgBkE8///oAWnF8b2bXP6nRv1mkXBv+xzwCFHHDPmhFPOOOeCSyZMmXHFNTfcMueOex545IlnXliw5JUVa95454NPvvjmh79+DXYzdNisbYdfSqMHMg== diff --git a/python/tests/reference/Geom/clean_4_None_False.vtr b/python/tests/reference/Geom/clean_4_None_False.vtr index 686f5a190..91863290c 100644 --- a/python/tests/reference/Geom/clean_4_None_False.vtr +++ b/python/tests/reference/Geom/clean_4_None_False.vtr @@ -1,11 +1,16 @@ + + + AQAAAACAAABJAAAATwAAAA==eF4FwdEJgDAMBUBH6ad+JLzElmoXcI6grYKtCoLze7dZs/fkJd+N15rtct/IYJDV5zDSGGiPE6QEjcX1CgVhJlUnIakkLwQPDN0PHdcSuQ== + + - + AQAAAACAAAAABQAAIAAAAA==eF5jZIAAxlF6lB4AmokAPdj1DzRNyP2jNH4aAMufANU= diff --git a/python/tests/reference/Geom/clean_4_None_True.vtr b/python/tests/reference/Geom/clean_4_None_True.vtr index 66109b418..8e3375229 100644 --- a/python/tests/reference/Geom/clean_4_None_True.vtr +++ b/python/tests/reference/Geom/clean_4_None_True.vtr @@ -1,11 +1,16 @@ + + + AQAAAACAAABJAAAATwAAAA==eF4FwVEKgCAQBcCO4md97PJcE9MLdI6ltCCtIOj8zuza9Lt4zU/jrWa9ze8YDNL6nkoSPB1hgS1eQjGjQECIJGKsT2KTi4QZmIYOHg4SwA== + + - + AQAAAACAAAAABQAAMAAAAA==eF5jYoAAJhw0IwEalz566aeUptT+oa6fUppS+4e6fkppSu0f6voppSm1HwBAngDh diff --git a/python/tests/reference/Material/material.yaml b/python/tests/reference/Material/material.yaml new file mode 100644 index 000000000..1d3cf08a8 --- /dev/null +++ b/python/tests/reference/Material/material.yaml @@ -0,0 +1,42 @@ +homogenization: + SX: + mech: {type: none} + Taylor: + mech: {type: isostrain, N_constituents: 2} + +microstructure: + - constituents: + - fraction: 1.0 + orientation: [1.0, 0.0, 0.0, 0.0] + phase: Aluminum + homogenization: SX + - constituents: + - fraction: 1.0 + orientation: [0.7936696712125002, -0.28765777461664166, -0.3436487135089419, 0.4113964260949434] + phase: Aluminum + homogenization: SX + - constituents: + - fraction: 1.0 + orientation: [0.3986143167493579, -0.7014883552495493, 0.2154871765709027, 0.5500781677772945] + phase: Aluminum + homogenization: SX + - homogenization: Taylor + constituents: + - fraction: .5 + orientation: [0.28645844315788244, -0.022571491243423537, -0.467933059311115, -0.8357456192708106] + phase: Aluminum + - fraction: .5 + orientation: [0.3986143167493579, -0.7014883552495493, 0.2154871765709027, 0.5500781677772945] + phase: Steel + +phase: + Aluminum: + elasticity: {C_11: 106.75e9, C_12: 60.41e9, C_44: 28.34e9, type: hooke} + generic: + output: [F, P, Fe, Fp, Lp] + lattice: fcc + Steel: + elasticity: {C_11: 233.3e9, C_12: 135.5e9, C_44: 118.0e9, type: hooke} + generic: + output: [F, P, Fe, Fp, Lp] + lattice: bcc diff --git a/python/tests/reference/Rotation/Visualize_hybridIA_sampling.ipynb b/python/tests/reference/Rotation/Visualize_hybridIA_sampling.ipynb new file mode 100644 index 000000000..2f6b8cb22 --- /dev/null +++ b/python/tests/reference/Rotation/Visualize_hybridIA_sampling.ipynb @@ -0,0 +1,276 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "import damask\n", + "\n", + "from pathlib import Path" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "orientations,rODF = damask.Rotation.from_ODF('hybridIA_ODF.txt',\n", + " 2**14,\n", + " degrees=True,\n", + " reconstruct=True,\n", + " fractions=True,\n", + " seed=0)" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [], + "source": [ + "VTK = damask.VTK.from_rectilinearGrid([36,36,36],[90,90,90])\n", + "VTK.add(damask.Table.from_ASCII('hybridIA_ODF.txt').get('intensity'),'intensity')\n", + "VTK.add(rODF.flatten(order='F'),'rODF')\n", + "VTK.to_file('hybridIA_ODF.vtr')" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Help on class VTK in module damask._vtk:\n", + "\n", + "class VTK(builtins.object)\n", + " | VTK(geom)\n", + " | \n", + " | Spatial visualization (and potentially manipulation).\n", + " | \n", + " | High-level interface to VTK.\n", + " | \n", + " | Methods defined here:\n", + " | \n", + " | __init__(self, geom)\n", + " | Set geometry and topology.\n", + " | \n", + " | Parameters\n", + " | ----------\n", + " | geom : subclass of vtk.vtkDataSet\n", + " | Description of geometry and topology. Valid types are vtk.vtkRectilinearGrid,\n", + " | vtk.vtkUnstructuredGrid, or vtk.vtkPolyData.\n", + " | \n", + " | __repr__(self)\n", + " | ASCII representation of the VTK data.\n", + " | \n", + " | add(self, data, label=None)\n", + " | Add data to either cells or points.\n", + " | \n", + " | Parameters\n", + " | ----------\n", + " | data : numpy.ndarray\n", + " | Data to add. First dimension need to match either\n", + " | number of cells or number of points\n", + " | label : str\n", + " | Data label.\n", + " | \n", + " | add_comments(self, comments)\n", + " | Add Comments.\n", + " | \n", + " | Parameters\n", + " | ----------\n", + " | comments : str or list of str\n", + " | Comments to add.\n", + " | \n", + " | get(self, label)\n", + " | Get either cell or point data.\n", + " | \n", + " | Cell data takes precedence over point data, i.e. this\n", + " | function assumes that labels are unique among cell and\n", + " | point data.\n", + " | \n", + " | Parameters\n", + " | ----------\n", + " | label : str\n", + " | Data label.\n", + " | \n", + " | get_comments(self)\n", + " | Return the comments.\n", + " | \n", + " | set_comments(self, comments)\n", + " | Set Comments.\n", + " | \n", + " | Parameters\n", + " | ----------\n", + " | comments : str or list of str\n", + " | Comments.\n", + " | \n", + " | show(self)\n", + " | Render.\n", + " | \n", + " | See http://compilatrix.com/article/vtk-1 for further ideas.\n", + " | \n", + " | write(self, fname, parallel=True)\n", + " | Write to file.\n", + " | \n", + " | Parameters\n", + " | ----------\n", + " | fname : str or pathlib.Path\n", + " | Filename for writing.\n", + " | parallel : boolean, optional\n", + " | Write data in parallel background process. Defaults to True.\n", + " | \n", + " | ----------------------------------------------------------------------\n", + " | Static methods defined here:\n", + " | \n", + " | from_file(fname, dataset_type=None)\n", + " | Create VTK from file.\n", + " | \n", + " | Parameters\n", + " | ----------\n", + " | fname : str or pathlib.Path\n", + " | Filename for reading. Valid extensions are .vtr, .vtu, .vtp, and .vtk.\n", + " | dataset_type : str, optional\n", + " | Name of the vtk.vtkDataSet subclass when opening an .vtk file. Valid types are vtkRectilinearGrid,\n", + " | vtkUnstructuredGrid, and vtkPolyData.\n", + " | \n", + " | from_polyData(points)\n", + " | Create VTK of type vtk.polyData.\n", + " | \n", + " | This is the common type for point-wise data.\n", + " | \n", + " | Parameters\n", + " | ----------\n", + " | points : numpy.ndarray of shape (:,3)\n", + " | Spatial position of the points.\n", + " | \n", + " | from_rectilinearGrid(grid, size, origin=array([0., 0., 0.]))\n", + " | Create VTK of type vtk.vtkRectilinearGrid.\n", + " | \n", + " | This is the common type for results from the grid solver.\n", + " | \n", + " | Parameters\n", + " | ----------\n", + " | grid : numpy.ndarray of shape (3) of np.dtype = int\n", + " | Number of cells.\n", + " | size : numpy.ndarray of shape (3)\n", + " | Physical length.\n", + " | origin : numpy.ndarray of shape (3), optional\n", + " | Spatial origin.\n", + " | \n", + " | from_unstructuredGrid(nodes, connectivity, cell_type)\n", + " | Create VTK of type vtk.vtkUnstructuredGrid.\n", + " | \n", + " | This is the common type for results from FEM solvers.\n", + " | \n", + " | Parameters\n", + " | ----------\n", + " | nodes : numpy.ndarray of shape (:,3)\n", + " | Spatial position of the nodes.\n", + " | connectivity : numpy.ndarray of np.dtype = int\n", + " | Cell connectivity (0-based), first dimension determines #Cells, second dimension determines #Nodes/Cell.\n", + " | cell_type : str\n", + " | Name of the vtk.vtkCell subclass. Tested for TRIANGLE, QUAD, TETRA, and HEXAHEDRON.\n", + " | \n", + " | ----------------------------------------------------------------------\n", + " | Data descriptors defined here:\n", + " | \n", + " | __dict__\n", + " | dictionary for instance variables (if defined)\n", + " | \n", + " | __weakref__\n", + " | list of weak references to the object (if defined)\n", + "\n" + ] + } + ], + "source": [ + "help(damask.VTK)" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [], + "source": [ + "a,b=np.radians(([90,90],[45,45]))" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([1.57079633, 1.57079633])" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([0.78539816, 0.78539816])" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "b" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.5" + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/python/tests/test_Colormap.py b/python/tests/test_Colormap.py index 22c208551..7f12469f8 100644 --- a/python/tests/test_Colormap.py +++ b/python/tests/test_Colormap.py @@ -75,41 +75,36 @@ class TestColormap: assert np.allclose(Colormap._xyz2msh(xyz),msh,atol=1.e-6,rtol=0) - @pytest.mark.parametrize('format',['ASCII','paraview','GOM','Gmsh']) + @pytest.mark.parametrize('format',['ASCII','paraview','GOM','gmsh']) @pytest.mark.parametrize('model',['rgb','hsv','hsl','xyz','lab','msh']) def test_from_range(self,model,format,tmpdir): N = np.random.randint(2,256) - c = Colormap.from_range(np.random.rand(3),np.random.rand(3),model=model,N=N) - c.to_file(tmpdir/'color_out',format=format) + c = Colormap.from_range(np.random.rand(3),np.random.rand(3),model=model,N=N) # noqa + eval(f'c.save_{format}(tmpdir/"color_out")') - @pytest.mark.parametrize('format',['ASCII','paraview','GOM','Gmsh']) + @pytest.mark.parametrize('format',['ASCII','paraview','GOM','gmsh']) @pytest.mark.parametrize('name',['strain','gnuplot','Greys','PRGn','viridis']) def test_from_predefined(self,name,format,tmpdir): N = np.random.randint(2,256) - c = Colormap.from_predefined(name,N) + c = Colormap.from_predefined(name,N) # noqa os.chdir(tmpdir) - c.to_file(format=format) + eval(f'c.save_{format}()') @pytest.mark.parametrize('format,name',[('ASCII','test.txt'), ('paraview','test.json'), ('GOM','test.legend'), - ('Gmsh','test.msh') + ('gmsh','test.msh') ]) def test_write_filehandle(self,format,name,tmpdir): - c = Colormap.from_predefined('Dark2') + c = Colormap.from_predefined('Dark2') # noqa fname = tmpdir/name - with open(fname,'w') as f: - c.to_file(f,format=format) + with open(fname,'w') as f: # noqa + eval(f'c.save_{format}(f)') for i in range(10): if fname.exists(): return time.sleep(.5) assert False - def test_write_invalid_format(self): - c = Colormap.from_predefined('Dark2') - with pytest.raises(ValueError): - c.to_file(format='invalid') - @pytest.mark.parametrize('model',['rgb','hsv','hsl','lab','invalid']) def test_invalid_color(self,model): with pytest.raises(ValueError): @@ -119,13 +114,13 @@ class TestColormap: c_1 = Colormap.from_predefined('stress') c_2 = c_1.reversed() assert (not np.allclose(c_1.colors,c_2.colors)) and \ - np.allclose(c_1.colors,c_2.reversed().colors) + np.allclose(c_1.colors,c_2.reversed().colors) def test_invert(self): c_1 = Colormap.from_predefined('strain') c_2 = ~c_1 - assert (not np.allclose(c_1.colors,c_2.colors)) and \ - np.allclose(c_1.colors,(~c_2).colors) + assert (not np.allclose(c_1.colors, c_2.colors)) and \ + np.allclose(c_1.colors,(~c_2).colors) def test_add(self): c = Colormap.from_predefined('jet') @@ -149,16 +144,16 @@ class TestColormap: @pytest.mark.parametrize('format,ext',[('ASCII','.txt'), ('paraview','.json'), ('GOM','.legend'), - ('Gmsh','.msh') + ('gmsh','.msh') ]) def test_compare_reference(self,format,ext,tmpdir,reference_dir,update): name = 'binary' - c = Colormap.from_predefined(name) + c = Colormap.from_predefined(name) # noqa if update: os.chdir(reference_dir) - c.to_file(format=format) + eval(f'c.save_{format}()') else: os.chdir(tmpdir) - c.to_file(format=format) + eval(f'c.save_{format}()') time.sleep(.5) assert filecmp.cmp(tmpdir/(name+ext),reference_dir/(name+ext)) diff --git a/python/tests/test_Geom.py b/python/tests/test_Geom.py index 809aee2f9..961d28713 100644 --- a/python/tests/test_Geom.py +++ b/python/tests/test_Geom.py @@ -43,34 +43,26 @@ class TestGeom: def test_write_read_str(self,default,tmpdir): - default.to_file(str(tmpdir/'default.geom'),format='ASCII') - new = Geom.from_file(str(tmpdir/'default.geom')) + default.save_ASCII(str(tmpdir/'default.geom')) + new = Geom.load_ASCII(str(tmpdir/'default.geom')) assert geom_equal(default,new) def test_write_read_file(self,default,tmpdir): with open(tmpdir/'default.geom','w') as f: - default.to_file(f,format='ASCII',pack=True) + default.save_ASCII(f,compress=True) with open(tmpdir/'default.geom') as f: - new = Geom.from_file(f) - assert geom_equal(default,new) - - - def test_write_as_ASCII(self,default,tmpdir): - with open(tmpdir/'str.geom','w') as f: - f.write(default.as_ASCII()) - with open(tmpdir/'str.geom') as f: - new = Geom.from_file(f) + new = Geom.load_ASCII(f) assert geom_equal(default,new) def test_read_write_vtr(self,default,tmpdir): - default.to_file(tmpdir/'default',format='vtr') + default.save(tmpdir/'default') for _ in range(10): time.sleep(.2) if os.path.exists(tmpdir/'default.vtr'): break - new = Geom.from_vtr(tmpdir/'default.vtr') + new = Geom.load(tmpdir/'default.vtr') assert geom_equal(new,default) @@ -79,23 +71,23 @@ class TestGeom: f.write('this is not a valid header') with open('invalid_file','r') as f: with pytest.raises(TypeError): - Geom.from_file(f) + Geom.load_ASCII(f) def test_invalid_vtr(self,tmpdir): v = VTK.from_rectilinearGrid(np.random.randint(5,10,3)*2,np.random.random(3) + 1.0) - v.to_file(tmpdir/'no_materialpoint.vtr') + v.save(tmpdir/'no_materialpoint.vtr') for _ in range(10): time.sleep(.2) if os.path.exists(tmpdir/'no_materialpoint.vtr'): break with pytest.raises(ValueError): - Geom.from_vtr(tmpdir/'no_materialpoint.vtr') + Geom.load(tmpdir/'no_materialpoint.vtr') - @pytest.mark.parametrize('pack',[True,False]) - def test_pack(self,default,tmpdir,pack): - default.to_file(tmpdir/'default.geom',format='ASCII',pack=pack) - new = Geom.from_file(tmpdir/'default.geom') + @pytest.mark.parametrize('compress',[True,False]) + def test_compress(self,default,tmpdir,compress): + default.save_ASCII(tmpdir/'default.geom',compress=compress) + new = Geom.load_ASCII(tmpdir/'default.geom') assert geom_equal(new,default) @@ -125,11 +117,6 @@ class TestGeom: Geom(materials) - def test_invalid_write_format(self,default): - with pytest.raises(TypeError): - default.to_file(format='invalid') - - @pytest.mark.parametrize('directions,reflect',[ (['x'], False), (['x','y','z'],True), @@ -141,8 +128,8 @@ class TestGeom: modified = default.mirror(directions,reflect) tag = f'directions={"-".join(directions)}_reflect={reflect}' reference = reference_dir/f'mirror_{tag}.geom' - if update: modified.to_file(reference) - assert geom_equal(Geom.from_file(reference), + if update: modified.save_ASCII(reference) + assert geom_equal(Geom.load_ASCII(reference), modified) @@ -163,8 +150,8 @@ class TestGeom: modified = default.flip(directions) tag = f'directions={"-".join(directions)}' reference = reference_dir/f'flip_{tag}.geom' - if update: modified.to_file(reference) - assert geom_equal(Geom.from_file(reference), + if update: modified.save_ASCII(reference) + assert geom_equal(Geom.load_ASCII(reference), modified) @@ -190,11 +177,11 @@ class TestGeom: current = default.clean(stencil,selection,periodic) reference = reference_dir/f'clean_{stencil}_{"+".join(map(str,[None] if selection is None else selection))}_{periodic}' if update and stencil > 1: - current.to_file(reference,format='vtr') + current.save(reference) for _ in range(10): time.sleep(.2) if os.path.exists(reference.with_suffix('.vtr')): break - assert geom_equal(Geom.from_vtr(reference) if stencil > 1 else default, + assert geom_equal(Geom.load(reference) if stencil > 1 else default, current ) @@ -212,8 +199,8 @@ class TestGeom: modified = default.scale(grid) tag = f'grid={util.srepr(grid,"-")}' reference = reference_dir/f'scale_{tag}.geom' - if update: modified.to_file(reference) - assert geom_equal(Geom.from_file(reference), + if update: modified.save_ASCII(reference) + assert geom_equal(Geom.load_ASCII(reference), modified) @@ -255,8 +242,8 @@ class TestGeom: modified = default.rotate(Rotation.from_Eulers(Eulers,degrees=True)) tag = f'Eulers={util.srepr(Eulers,"-")}' reference = reference_dir/f'rotate_{tag}.geom' - if update: modified.to_file(reference) - assert geom_equal(Geom.from_file(reference), + if update: modified.save_ASCII(reference) + assert geom_equal(Geom.load_ASCII(reference), modified) diff --git a/python/tests/test_Material.py b/python/tests/test_Material.py new file mode 100644 index 000000000..567dfe646 --- /dev/null +++ b/python/tests/test_Material.py @@ -0,0 +1,61 @@ +import os + +import pytest + +from damask import Material + +@pytest.fixture +def reference_dir(reference_dir_base): + """Directory containing reference results.""" + return reference_dir_base/'Material' + + +class TestMaterial: + + @pytest.mark.parametrize('fname',[None,'test.yaml']) + def test_load_save(self,reference_dir,tmp_path,fname): + reference = Material.load(reference_dir/'material.yaml') + os.chdir(tmp_path) + if fname is None: + reference.save() + new = Material.load('material.yaml') + else: + reference.save(fname) + new = Material.load(fname) + assert reference == new + + def test_valid_complete(self,reference_dir): + material_config = Material.load(reference_dir/'material.yaml') + assert material_config.is_valid and material_config.is_complete + + def test_invalid_lattice(self,reference_dir): + material_config = Material.load(reference_dir/'material.yaml') + material_config['phase']['Aluminum']['lattice']='fxc' + assert not material_config.is_valid + + def test_invalid_orientation(self,reference_dir): + material_config = Material.load(reference_dir/'material.yaml') + material_config['microstructure'][0]['constituents'][0]['orientation']=[0,0,0,0] + assert not material_config.is_valid + + def test_invalid_fraction(self,reference_dir): + material_config = Material.load(reference_dir/'material.yaml') + material_config['microstructure'][0]['constituents'][0]['fraction']=.9 + assert not material_config.is_valid + + + @pytest.mark.parametrize('item',['homogenization','phase','microstructure']) + def test_incomplete_missing(self,reference_dir,item): + material_config = Material.load(reference_dir/'material.yaml') + del material_config[item] + assert not material_config.is_complete + + def test_incomplete_wrong_phase(self,reference_dir): + material_config = Material.load(reference_dir/'material.yaml') + new = material_config.microstructure_rename_phase({'Steel':'FeNbC'}) + assert not new.is_complete + + def test_incomplete_wrong_homogenization(self,reference_dir): + material_config = Material.load(reference_dir/'material.yaml') + new = material_config.microstructure_rename_homogenization({'Taylor':'isostrain'}) + assert not new.is_complete diff --git a/python/tests/test_Orientation.py b/python/tests/test_Orientation.py index 9a23dc0ed..669f73e91 100644 --- a/python/tests/test_Orientation.py +++ b/python/tests/test_Orientation.py @@ -106,8 +106,8 @@ class TestOrientation: coords = np.array([(1,i+1) for i,x in enumerate(eu)]) table = Table(eu,{'Eulers':(3,)}) table = table.add('pos',coords) - table.to_ASCII(reference) - assert np.allclose(eu,Table.from_ASCII(reference).get('Eulers')) + table.save(reference) + assert np.allclose(eu,Table.load(reference).get('Eulers')) @pytest.mark.parametrize('lattice',Lattice.lattices) def test_disorientation360(self,lattice): @@ -129,4 +129,3 @@ class TestOrientation: eqs = [r for r in R_1.equivalent] R_2 = Orientation.from_average(eqs) assert np.allclose(R_1.rotation.quaternion,R_2.rotation.quaternion) - diff --git a/python/tests/test_Result.py b/python/tests/test_Result.py index 6000f50f9..68b72badf 100644 --- a/python/tests/test_Result.py +++ b/python/tests/test_Result.py @@ -339,8 +339,8 @@ class TestResult: @pytest.mark.parametrize('output',['F',[],['F','P']]) def test_vtk(self,tmp_path,default,output): os.chdir(tmp_path) - default.to_vtk(output) + default.save_vtk(output) def test_XDMF(self,tmp_path,single_phase): os.chdir(tmp_path) - single_phase.write_XDMF() + single_phase.save_XDMF() diff --git a/python/tests/test_Rotation.py b/python/tests/test_Rotation.py index 5435895a2..66cabfbd4 100644 --- a/python/tests/test_Rotation.py +++ b/python/tests/test_Rotation.py @@ -461,7 +461,7 @@ def mul(me, other): if other.shape == (3,): A = me.quaternion[0]**2.0 - np.dot(me.quaternion[1:],me.quaternion[1:]) B = 2.0 * np.dot(me.quaternion[1:],other) - C = 2.0 * _P*me.quaternion[0] + C = 2.0 * _P * me.quaternion[0] return A*other + B*me.quaternion[1:] + C * np.cross(me.quaternion[1:],other) @@ -496,9 +496,8 @@ class TestRotation: o = backward(forward(m)) ok = np.allclose(m,o,atol=atol) if np.isclose(rot.as_quaternion()[0],0.0,atol=atol): - ok = ok or np.allclose(m*-1.,o,atol=atol) - print(m,o,rot.as_quaternion()) - assert ok and np.isclose(np.linalg.norm(o),1.0) + ok |= np.allclose(m*-1.,o,atol=atol) + assert ok and np.isclose(np.linalg.norm(o),1.0), f'{m},{o},{rot.as_quaternion()}' @pytest.mark.parametrize('forward,backward',[(Rotation._om2qu,Rotation._qu2om), (Rotation._om2eu,Rotation._eu2om), @@ -512,8 +511,7 @@ class TestRotation: m = rot.as_matrix() o = backward(forward(m)) ok = np.allclose(m,o,atol=atol) - print(m,o,rot.as_quaternion()) - assert ok and np.isclose(np.linalg.det(o),1.0) + assert ok and np.isclose(np.linalg.det(o),1.0), f'{m},{o},{rot.as_quaternion()}' @pytest.mark.parametrize('forward,backward',[(Rotation._eu2qu,Rotation._qu2eu), (Rotation._eu2om,Rotation._om2eu), @@ -531,9 +529,9 @@ class TestRotation: ok = ok or np.allclose(np.where(np.isclose(m,u),m-u,m),np.where(np.isclose(o,u),o-u,o),atol=atol) if np.isclose(m[1],0.0,atol=atol) or np.isclose(m[1],np.pi,atol=atol): sum_phi = np.unwrap([m[0]+m[2],o[0]+o[2]]) - ok = ok or np.isclose(sum_phi[0],sum_phi[1],atol=atol) - print(m,o,rot.as_quaternion()) - assert ok and (np.zeros(3)-1.e-9 <= o).all() and (o <= np.array([np.pi*2.,np.pi,np.pi*2.])+1.e-9).all() + ok |= np.isclose(sum_phi[0],sum_phi[1],atol=atol) + assert ok and (np.zeros(3)-1.e-9 <= o).all() \ + and (o <= np.array([np.pi*2.,np.pi,np.pi*2.])+1.e-9).all(), f'{m},{o},{rot.as_quaternion()}' @pytest.mark.parametrize('forward,backward',[(Rotation._ax2qu,Rotation._qu2ax), (Rotation._ax2om,Rotation._om2ax), @@ -548,9 +546,8 @@ class TestRotation: o = backward(forward(m)) ok = np.allclose(m,o,atol=atol) if np.isclose(m[3],np.pi,atol=atol): - ok = ok or np.allclose(m*np.array([-1.,-1.,-1.,1.]),o,atol=atol) - print(m,o,rot.as_quaternion()) - assert ok and np.isclose(np.linalg.norm(o[:3]),1.0) and o[3]<=np.pi+1.e-9 + ok |= np.allclose(m*np.array([-1.,-1.,-1.,1.]),o,atol=atol) + assert ok and np.isclose(np.linalg.norm(o[:3]),1.0) and o[3]<=np.pi+1.e-9, f'{m},{o},{rot.as_quaternion()}' @pytest.mark.parametrize('forward,backward',[(Rotation._ro2qu,Rotation._qu2ro), #(Rotation._ro2om,Rotation._om2ro), @@ -566,8 +563,7 @@ class TestRotation: o = backward(forward(m)) ok = np.allclose(np.clip(m,None,cutoff),np.clip(o,None,cutoff),atol=atol) ok = ok or np.isclose(m[3],0.0,atol=atol) - print(m,o,rot.as_quaternion()) - assert ok and np.isclose(np.linalg.norm(o[:3]),1.0) + assert ok and np.isclose(np.linalg.norm(o[:3]),1.0), f'{m},{o},{rot.as_quaternion()}' @pytest.mark.parametrize('forward,backward',[(Rotation._ho2qu,Rotation._qu2ho), (Rotation._ho2om,Rotation._om2ho), @@ -581,8 +577,7 @@ class TestRotation: m = rot.as_homochoric() o = backward(forward(m)) ok = np.allclose(m,o,atol=atol) - print(m,o,rot.as_quaternion()) - assert ok and np.linalg.norm(o) < _R1 + 1.e-9 + assert ok and np.linalg.norm(o) < _R1 + 1.e-9, f'{m},{o},{rot.as_quaternion()}' @pytest.mark.parametrize('forward,backward',[(Rotation._cu2qu,Rotation._qu2cu), (Rotation._cu2om,Rotation._om2cu), @@ -598,8 +593,7 @@ class TestRotation: ok = np.allclose(m,o,atol=atol) if np.count_nonzero(np.isclose(np.abs(o),np.pi**(2./3.)*.5)): ok = ok or np.allclose(m*-1.,o,atol=atol) - print(m,o,rot.as_quaternion()) - assert ok and np.max(np.abs(o)) < np.pi**(2./3.) * 0.5 + 1.e-9 + assert ok and np.max(np.abs(o)) < np.pi**(2./3.) * 0.5 + 1.e-9, f'{m},{o},{rot.as_quaternion()}' @pytest.mark.parametrize('vectorized, single',[(Rotation._qu2om,qu2om), (Rotation._qu2eu,qu2eu), @@ -612,8 +606,7 @@ class TestRotation: vectorized(qu.reshape(qu.shape[0]//2,-1,4)) co = vectorized(qu) for q,c in zip(qu,co): - print(q,c) - assert np.allclose(single(q),c) and np.allclose(single(q),vectorized(q)) + assert np.allclose(single(q),c) and np.allclose(single(q),vectorized(q)), f'{q},{c}' @pytest.mark.parametrize('vectorized, single',[(Rotation._om2qu,om2qu), @@ -625,8 +618,7 @@ class TestRotation: vectorized(om.reshape(om.shape[0]//2,-1,3,3)) co = vectorized(om) for o,c in zip(om,co): - print(o,c) - assert np.allclose(single(o),c) and np.allclose(single(o),vectorized(o)) + assert np.allclose(single(o),c) and np.allclose(single(o),vectorized(o)), f'{o},{c}' @pytest.mark.parametrize('vectorized, single',[(Rotation._eu2qu,eu2qu), (Rotation._eu2om,eu2om), @@ -638,8 +630,7 @@ class TestRotation: vectorized(eu.reshape(eu.shape[0]//2,-1,3)) co = vectorized(eu) for e,c in zip(eu,co): - print(e,c) - assert np.allclose(single(e),c) and np.allclose(single(e),vectorized(e)) + assert np.allclose(single(e),c) and np.allclose(single(e),vectorized(e)), f'{e},{c}' @pytest.mark.parametrize('vectorized, single',[(Rotation._ax2qu,ax2qu), (Rotation._ax2om,ax2om), @@ -651,8 +642,7 @@ class TestRotation: vectorized(ax.reshape(ax.shape[0]//2,-1,4)) co = vectorized(ax) for a,c in zip(ax,co): - print(a,c) - assert np.allclose(single(a),c) and np.allclose(single(a),vectorized(a)) + assert np.allclose(single(a),c) and np.allclose(single(a),vectorized(a)), f'{a},{c}' @pytest.mark.parametrize('vectorized, single',[(Rotation._ro2ax,ro2ax), @@ -663,8 +653,7 @@ class TestRotation: vectorized(ro.reshape(ro.shape[0]//2,-1,4)) co = vectorized(ro) for r,c in zip(ro,co): - print(r,c) - assert np.allclose(single(r),c) and np.allclose(single(r),vectorized(r)) + assert np.allclose(single(r),c) and np.allclose(single(r),vectorized(r)), f'{r},{c}' @pytest.mark.parametrize('vectorized, single',[(Rotation._ho2ax,ho2ax), (Rotation._ho2cu,ho2cu)]) @@ -674,8 +663,7 @@ class TestRotation: vectorized(ho.reshape(ho.shape[0]//2,-1,3)) co = vectorized(ho) for h,c in zip(ho,co): - print(h,c) - assert np.allclose(single(h),c) and np.allclose(single(h),vectorized(h)) + assert np.allclose(single(h),c) and np.allclose(single(h),vectorized(h)), f'{h},{c}' @pytest.mark.parametrize('vectorized, single',[(Rotation._cu2ho,cu2ho)]) def test_cubochoric_vectorization(self,set_of_rotations,vectorized,single): @@ -684,8 +672,7 @@ class TestRotation: vectorized(cu.reshape(cu.shape[0]//2,-1,3)) co = vectorized(cu) for u,c in zip(cu,co): - print(u,c) - assert np.allclose(single(u),c) and np.allclose(single(u),vectorized(u)) + assert np.allclose(single(u),c) and np.allclose(single(u),vectorized(u)), f'{u},{c}' @pytest.mark.parametrize('func',[Rotation.from_axis_angle]) def test_normalization_vectorization(self,func): @@ -703,9 +690,8 @@ class TestRotation: o = Rotation.from_Eulers(rot.as_Eulers(degrees),degrees).as_quaternion() ok = np.allclose(m,o,atol=atol) if np.isclose(rot.as_quaternion()[0],0.0,atol=atol): - ok = ok or np.allclose(m*-1.,o,atol=atol) - print(m,o,rot.as_quaternion()) - assert ok and np.isclose(np.linalg.norm(o),1.0) + ok |= np.allclose(m*-1.,o,atol=atol) + assert ok and np.isclose(np.linalg.norm(o),1.0), f'{m},{o},{rot.as_quaternion()}' @pytest.mark.parametrize('P',[1,-1]) @pytest.mark.parametrize('normalize',[True,False]) @@ -717,12 +703,12 @@ class TestRotation: o = Rotation.from_axis_angle(rot.as_axis_angle(degrees)*c,degrees,normalize,P).as_Eulers() u = np.array([np.pi*2,np.pi,np.pi*2]) ok = np.allclose(m,o,atol=atol) - ok = ok or np.allclose(np.where(np.isclose(m,u),m-u,m),np.where(np.isclose(o,u),o-u,o),atol=atol) + ok |= np.allclose(np.where(np.isclose(m,u),m-u,m),np.where(np.isclose(o,u),o-u,o),atol=atol) if np.isclose(m[1],0.0,atol=atol) or np.isclose(m[1],np.pi,atol=atol): sum_phi = np.unwrap([m[0]+m[2],o[0]+o[2]]) - ok = ok or np.isclose(sum_phi[0],sum_phi[1],atol=atol) - print(m,o,rot.as_quaternion()) - assert ok and (np.zeros(3)-1.e-9 <= o).all() and (o <= np.array([np.pi*2.,np.pi,np.pi*2.])+1.e-9).all() + ok |= np.isclose(sum_phi[0],sum_phi[1],atol=atol) + assert ok and (np.zeros(3)-1.e-9 <= o).all() \ + and (o <= np.array([np.pi*2.,np.pi,np.pi*2.])+1.e-9).all(), f'{m},{o},{rot.as_quaternion()}' def test_matrix(self,set_of_rotations): for rot in set_of_rotations: @@ -731,8 +717,8 @@ class TestRotation: ok = np.allclose(m,o,atol=atol) if np.isclose(m[3],np.pi,atol=atol): ok = ok or np.allclose(m*np.array([-1.,-1.,-1.,1.]),o,atol=atol) - print(m,o,rot.as_quaternion()) - assert ok and np.isclose(np.linalg.norm(o[:3]),1.0) and o[3]<=np.pi+1.e-9 + assert ok and np.isclose(np.linalg.norm(o[:3]),1.0) \ + and o[3]<=np.pi+1.e-9, f'{m},{o},{rot.as_quaternion()}' @pytest.mark.parametrize('P',[1,-1]) @pytest.mark.parametrize('normalize',[True,False]) @@ -742,8 +728,7 @@ class TestRotation: m = rot.as_matrix() o = Rotation.from_Rodrigues(rot.as_Rodrigues()*c,normalize,P).as_matrix() ok = np.allclose(m,o,atol=atol) - print(m,o) - assert ok and np.isclose(np.linalg.det(o),1.0) + assert ok and np.isclose(np.linalg.det(o),1.0), f'{m},{o}' @pytest.mark.parametrize('P',[1,-1]) def test_homochoric(self,set_of_rotations,P): @@ -753,8 +738,7 @@ class TestRotation: o = Rotation.from_homochoric(rot.as_homochoric()*P*-1,P).as_Rodrigues() ok = np.allclose(np.clip(m,None,cutoff),np.clip(o,None,cutoff),atol=atol) ok = ok or np.isclose(m[3],0.0,atol=atol) - print(m,o,rot.as_quaternion()) - assert ok and np.isclose(np.linalg.norm(o[:3]),1.0) + assert ok and np.isclose(np.linalg.norm(o[:3]),1.0), f'{m},{o},{rot.as_quaternion()}' @pytest.mark.parametrize('P',[1,-1]) def test_cubochoric(self,set_of_rotations,P): @@ -762,8 +746,7 @@ class TestRotation: m = rot.as_homochoric() o = Rotation.from_cubochoric(rot.as_cubochoric()*P*-1,P).as_homochoric() ok = np.allclose(m,o,atol=atol) - print(m,o,rot.as_quaternion()) - assert ok and np.linalg.norm(o) < (3.*np.pi/4.)**(1./3.) + 1.e-9 + assert ok and np.linalg.norm(o) < (3.*np.pi/4.)**(1./3.) + 1.e-9, f'{m},{o},{rot.as_quaternion()}' @pytest.mark.parametrize('P',[1,-1]) @pytest.mark.parametrize('accept_homomorph',[True,False]) @@ -774,9 +757,8 @@ class TestRotation: o = Rotation.from_quaternion(rot.as_quaternion()*c,accept_homomorph,P).as_cubochoric() ok = np.allclose(m,o,atol=atol) if np.count_nonzero(np.isclose(np.abs(o),np.pi**(2./3.)*.5)): - ok = ok or np.allclose(m*-1.,o,atol=atol) - print(m,o,rot.as_quaternion()) - assert ok and o.max() < np.pi**(2./3.)*0.5+1.e-9 + ok |= np.allclose(m*-1.,o,atol=atol) + assert ok and o.max() < np.pi**(2./3.)*0.5+1.e-9, f'{m},{o},{rot.as_quaternion()}' @pytest.mark.parametrize('reciprocal',[True,False]) def test_basis(self,set_of_rotations,reciprocal): @@ -858,8 +840,7 @@ class TestRotation: for rot in set_of_rotations: v = rot.broadcast_to((5,)) @ data for i in range(data.shape[0]): - print(i-data[i]) - assert np.allclose(mul(rot,data[i]),v[i]) + assert np.allclose(mul(rot,data[i]),v[i]), f'{i-data[i]}' @pytest.mark.parametrize('data',[np.random.rand(3), @@ -926,34 +907,39 @@ class TestRotation: @pytest.mark.parametrize('sigma',[5,10,15,20]) @pytest.mark.parametrize('N',[1000,10000,100000]) def test_spherical_component(self,N,sigma): - c = Rotation.from_random() - o = Rotation.from_spherical_component(c,sigma,N) - _, angles = c.misorientation(o).as_axis_angle(pair=True,degrees=True) - angles[::2] *= -1 # flip angle for every second to symmetrize distribution + p = [] + for run in range(5): + c = Rotation.from_random() + o = Rotation.from_spherical_component(c,sigma,N) + _, angles = c.misorientation(o).as_axis_angle(pair=True,degrees=True) + angles[::2] *= -1 # flip angle for every second to symmetrize distribution + + p.append(stats.normaltest(angles)[1]) - p = stats.normaltest(angles)[1] sigma_out = np.std(angles) - print(f'\np: {p}, sigma ratio {sigma/sigma_out}') - assert (.9 < sigma/sigma_out < 1.1) and p > 0.001 + p = np.average(p) + assert (.9 < sigma/sigma_out < 1.1) and p > 1e-2, f'{sigma/sigma_out},{p}' @pytest.mark.parametrize('sigma',[5,10,15,20]) @pytest.mark.parametrize('N',[1000,10000,100000]) def test_from_fiber_component(self,N,sigma): - """https://en.wikipedia.org/wiki/Full_width_at_half_maximum.""" - alpha = np.random.random(2)*np.pi - beta = np.random.random(2)*np.pi + p = [] + for run in range(5): + alpha = np.random.random()*2*np.pi,np.arccos(np.random.random()) + beta = np.random.random()*2*np.pi,np.arccos(np.random.random()) - f_in_C = np.array([np.sin(alpha[0])*np.cos(alpha[1]), np.sin(alpha[0])*np.sin(alpha[1]), np.cos(alpha[0])]) - f_in_S = np.array([np.sin(beta[0] )*np.cos(beta[1] ), np.sin(beta[0] )*np.sin(beta[1] ), np.cos(beta[0] )]) - ax = np.append(np.cross(f_in_C,f_in_S), - np.arccos(np.dot(f_in_C,f_in_S))) - n = Rotation.from_axis_angle(ax if ax[3] > 0.0 else ax*-1.0 ,normalize=True) # rotation to align fiber axis in crystal and sample system + f_in_C = np.array([np.sin(alpha[0])*np.cos(alpha[1]), np.sin(alpha[0])*np.sin(alpha[1]), np.cos(alpha[0])]) + f_in_S = np.array([np.sin(beta[0] )*np.cos(beta[1] ), np.sin(beta[0] )*np.sin(beta[1] ), np.cos(beta[0] )]) + ax = np.append(np.cross(f_in_C,f_in_S), - np.arccos(np.dot(f_in_C,f_in_S))) + n = Rotation.from_axis_angle(ax if ax[3] > 0.0 else ax*-1.0 ,normalize=True) # rotation to align fiber axis in crystal and sample system - o = Rotation.from_fiber_component(alpha,beta,np.radians(sigma),N,False) - angles = np.arccos(np.clip(np.dot(o@np.broadcast_to(f_in_S,(N,3)),n@f_in_S),-1,1)) - dist = np.array(angles) * (np.random.randint(0,2,N)*2-1) + o = Rotation.from_fiber_component(alpha,beta,np.radians(sigma),N,False) + angles = np.arccos(np.clip(np.dot(o@np.broadcast_to(f_in_S,(N,3)),n@f_in_S),-1,1)) + dist = np.array(angles) * (np.random.randint(0,2,N)*2-1) + + p.append(stats.normaltest(dist)[1]) - p = stats.normaltest(dist)[1] sigma_out = np.degrees(np.std(dist)) - print(f'\np: {p}, sigma ratio {sigma/sigma_out}') - assert (.9 < sigma/sigma_out < 1.1) and p > 0.001 + p = np.average(p) + assert (.9 < sigma/sigma_out < 1.1) and p > 1e-2, f'{sigma/sigma_out},{p}' diff --git a/python/tests/test_Table.py b/python/tests/test_Table.py index 1763e27ef..7a86c7fed 100644 --- a/python/tests/test_Table.py +++ b/python/tests/test_Table.py @@ -35,50 +35,50 @@ class TestTable: @pytest.mark.parametrize('mode',['str','path']) def test_write_read(self,default,tmpdir,mode): - default.to_file(tmpdir/'default.txt') + default.save(tmpdir/'default.txt') if mode == 'path': - new = Table.from_ASCII(tmpdir/'default.txt') + new = Table.load(tmpdir/'default.txt') elif mode == 'str': - new = Table.from_ASCII(str(tmpdir/'default.txt')) + new = Table.load(str(tmpdir/'default.txt')) assert all(default.data==new.data) and default.shapes == new.shapes def test_write_read_file(self,default,tmpdir): with open(tmpdir/'default.txt','w') as f: - default.to_file(f) + default.save(f) with open(tmpdir/'default.txt') as f: - new = Table.from_ASCII(f) + new = Table.load(f) assert all(default.data==new.data) and default.shapes == new.shapes - def test_write_read_new_style(self,default,tmpdir): - with open(tmpdir/'new_style.txt','w') as f: - default.to_file(f,new_style=True) - with open(tmpdir/'new_style.txt') as f: - new = Table.from_ASCII(f) + def test_write_read_legacy_style(self,default,tmpdir): + with open(tmpdir/'legacy.txt','w') as f: + default.save(f,legacy=True) + with open(tmpdir/'legacy.txt') as f: + new = Table.load(f) assert all(default.data==new.data) and default.shapes == new.shapes def test_write_invalid_format(self,default,tmpdir): with pytest.raises(TypeError): - default.to_file(tmpdir/'shouldnotbethere.txt',format='invalid') + default.save(tmpdir/'shouldnotbethere.txt',format='invalid') @pytest.mark.parametrize('mode',['str','path']) def test_read_ang(self,reference_dir,mode): if mode == 'path': - new = Table.from_ang(reference_dir/'simple.ang') + new = Table.load_ang(reference_dir/'simple.ang') elif mode == 'str': - new = Table.from_ang(str(reference_dir/'simple.ang')) + new = Table.load_ang(str(reference_dir/'simple.ang')) assert new.data.shape == (4,10) and \ new.labels == ['eu', 'pos', 'IQ', 'CI', 'ID', 'intensity', 'fit'] def test_read_ang_file(self,reference_dir): f = open(reference_dir/'simple.ang') - new = Table.from_ang(f) + new = Table.load_ang(f) assert new.data.shape == (4,10) and \ new.labels == ['eu', 'pos', 'IQ', 'CI', 'ID', 'intensity', 'fit'] @pytest.mark.parametrize('fname',['datatype-mix.txt','whitespace-mix.txt']) def test_read_strange(self,reference_dir,fname): with open(reference_dir/fname) as f: - Table.from_ASCII(f) + Table.load(f) def test_set(self,default): d = default.set('F',np.zeros((5,3,3)),'set to zero').get('F') @@ -166,7 +166,7 @@ class TestTable: x = np.random.random((5,12)) t = Table(x,{'F':(3,3),'v':(3,)},['random test data']) unsort = t.get('4_F') - sort = t.sort_by('4_F').get('4_F') + sort = t.sort_by('4_F').get('4_F') assert np.all(np.sort(unsort,0)==sort) def test_sort_revert(self): @@ -179,6 +179,6 @@ class TestTable: t = Table(np.array([[0,1,],[2,1,]]), {'v':(2,)}, ['test data'])\ - .add('s',np.array(['b','a']))\ - .sort_by('s') + .add('s',np.array(['b','a']))\ + .sort_by('s') assert np.all(t.get('1_v') == np.array([2,0]).reshape(2,1)) diff --git a/python/tests/test_VTK.py b/python/tests/test_VTK.py index f6be4da25..81c9eb772 100644 --- a/python/tests/test_VTK.py +++ b/python/tests/test_VTK.py @@ -32,22 +32,22 @@ class TestVTK: origin = np.random.random(3) v = VTK.from_rectilinearGrid(grid,size,origin) string = v.__repr__() - v.to_file(tmp_path/'rectilinearGrid',False) - vtr = VTK.from_file(tmp_path/'rectilinearGrid.vtr') + v.save(tmp_path/'rectilinearGrid',False) + vtr = VTK.load(tmp_path/'rectilinearGrid.vtr') with open(tmp_path/'rectilinearGrid.vtk','w') as f: f.write(string) - vtk = VTK.from_file(tmp_path/'rectilinearGrid.vtk','VTK_rectilinearGrid') + vtk = VTK.load(tmp_path/'rectilinearGrid.vtk','VTK_rectilinearGrid') assert(string == vtr.__repr__() == vtk.__repr__()) def test_polyData(self,tmp_path): points = np.random.rand(100,3) v = VTK.from_polyData(points) string = v.__repr__() - v.to_file(tmp_path/'polyData',False) - vtp = VTK.from_file(tmp_path/'polyData.vtp') + v.save(tmp_path/'polyData',False) + vtp = VTK.load(tmp_path/'polyData.vtp') with open(tmp_path/'polyData.vtk','w') as f: f.write(string) - vtk = VTK.from_file(tmp_path/'polyData.vtk','polyData') + vtk = VTK.load(tmp_path/'polyData.vtk','polyData') assert(string == vtp.__repr__() == vtk.__repr__()) @pytest.mark.parametrize('cell_type,n',[ @@ -62,11 +62,11 @@ class TestVTK: connectivity = np.random.choice(np.arange(n),n,False).reshape(-1,n) v = VTK.from_unstructuredGrid(nodes,connectivity,cell_type) string = v.__repr__() - v.to_file(tmp_path/'unstructuredGrid',False) - vtu = VTK.from_file(tmp_path/'unstructuredGrid.vtu') + v.save(tmp_path/'unstructuredGrid',False) + vtu = VTK.load(tmp_path/'unstructuredGrid.vtu') with open(tmp_path/'unstructuredGrid.vtk','w') as f: f.write(string) - vtk = VTK.from_file(tmp_path/'unstructuredGrid.vtk','unstructuredgrid') + vtk = VTK.load(tmp_path/'unstructuredGrid.vtk','unstructuredgrid') assert(string == vtu.__repr__() == vtk.__repr__()) @@ -75,8 +75,8 @@ class TestVTK: v = VTK.from_polyData(points) fname_s = tmp_path/'single.vtp' fname_p = tmp_path/'parallel.vtp' - v.to_file(fname_s,False) - v.to_file(fname_p,True) + v.save(fname_s,False) + v.save(fname_p,True) for i in range(10): if os.path.isfile(fname_p) and filecmp.cmp(fname_s,fname_p): assert(True) @@ -90,11 +90,11 @@ class TestVTK: ('this_file_does_not_exist.vtx', None)]) def test_invalid_dataset_type(self,name,dataset_type): with pytest.raises(TypeError): - VTK.from_file(name,dataset_type) + VTK.load(name,dataset_type) def test_invalid_extension_write(self,default): with pytest.raises(ValueError): - default.to_file('default.txt') + default.save('default.txt') def test_invalid_get(self,default): with pytest.raises(ValueError): @@ -115,8 +115,8 @@ class TestVTK: def test_comments(self,tmp_path,default): default.add_comments(['this is a comment']) - default.to_file(tmp_path/'with_comments',parallel=False) - new = VTK.from_file(tmp_path/'with_comments.vtr') + default.save(tmp_path/'with_comments',parallel=False) + new = VTK.load(tmp_path/'with_comments.vtr') assert new.get_comments() == ['this is a comment'] def test_compare_reference_polyData(self,update,reference_dir,tmp_path): @@ -124,9 +124,9 @@ class TestVTK: polyData = VTK.from_polyData(points) polyData.add(points,'coordinates') if update: - polyData.to_file(reference_dir/'polyData') + polyData.save(reference_dir/'polyData') else: - reference = VTK.from_file(reference_dir/'polyData.vtp') + reference = VTK.load(reference_dir/'polyData.vtp') assert polyData.__repr__() == reference.__repr__() and \ np.allclose(polyData.get('coordinates'),points) @@ -139,8 +139,8 @@ class TestVTK: rectilinearGrid.add(c,'cell') rectilinearGrid.add(n,'node') if update: - rectilinearGrid.to_file(reference_dir/'rectilinearGrid') + rectilinearGrid.save(reference_dir/'rectilinearGrid') else: - reference = VTK.from_file(reference_dir/'rectilinearGrid.vtr') + reference = VTK.load(reference_dir/'rectilinearGrid.vtr') assert rectilinearGrid.__repr__() == reference.__repr__() and \ np.allclose(rectilinearGrid.get('cell'),c) diff --git a/python/tests/test_util.py b/python/tests/test_util.py index c786e0de1..053045741 100644 --- a/python/tests/test_util.py +++ b/python/tests/test_util.py @@ -18,8 +18,8 @@ class TestUtil: @pytest.mark.parametrize('input,output', [ - ([2,0],[1,0]), - ([0.5,0.5],[1,1]), + ([0,-2],[0,-1]), + ([-0.5,0.5],[-1,1]), ([1./2.,1./3.],[3,2]), ([2./3.,1./2.,1./3.],[4,3,2]), ]) @@ -30,4 +30,4 @@ class TestUtil: def test_lackofprecision(self): with pytest.raises(ValueError): - util.scale_to_coprime(np.array([1/3333,1,1])) + util.scale_to_coprime(np.array([1/333.333,1,1]))