2013-06-21 01:15:25 +05:30
#!/usr/bin/env python
# -*- coding: UTF-8 no BOM -*-
2014-11-18 13:30:45 +05:30
import os , sys , string , math
2014-06-18 14:30:57 +05:30
import numpy as np
from optparse import OptionParser
2013-06-21 22:29:49 +05:30
from scipy import ndimage
2014-06-18 14:30:57 +05:30
import damask
2013-06-21 01:15:25 +05:30
2014-11-17 03:14:46 +05:30
scriptID = string . replace ( ' $Id$ ' , ' \n ' , ' \\ n ' )
scriptName = scriptID . split ( ) [ 1 ] [ : - 3 ]
2013-07-18 18:58:54 +05:30
2013-06-21 01:15:25 +05:30
#--------------------------------------------------------------------------------------------------
# MAIN
#--------------------------------------------------------------------------------------------------
2013-06-30 19:21:21 +05:30
synonyms = {
' grid ' : [ ' resolution ' ] ,
' size ' : [ ' dimension ' ] ,
}
2013-06-21 01:15:25 +05:30
identifiers = {
' grid ' : [ ' a ' , ' b ' , ' c ' ] ,
' size ' : [ ' x ' , ' y ' , ' z ' ] ,
' origin ' : [ ' x ' , ' y ' , ' z ' ] ,
}
mappings = {
2013-06-30 19:21:21 +05:30
' grid ' : lambda x : int ( x ) ,
' size ' : lambda x : float ( x ) ,
' origin ' : lambda x : float ( x ) ,
' homogenization ' : lambda x : int ( x ) ,
' microstructures ' : lambda x : int ( x ) ,
2013-06-21 01:15:25 +05:30
}
2014-06-18 14:30:57 +05:30
parser = OptionParser ( option_class = damask . extendableOption , usage = ' % prog options [file[s]] ' , description = """
2013-06-22 00:38:20 +05:30
Smoothens out interface roughness by simulated curvature flow .
2013-06-30 19:21:21 +05:30
This is achieved by the diffusion of each initially sharply bounded grain volume within the periodic domain
up to a given distance ' d ' voxels .
The final geometry is assembled by selecting at each voxel that grain index for which the concentration remains largest .
2013-07-18 18:58:54 +05:30
""" + string.replace(scriptID, ' \n ' , ' \\ n ' )
2013-06-21 01:15:25 +05:30
)
2014-06-07 23:43:29 +05:30
parser . add_option ( ' -d ' , ' --distance ' , dest = ' d ' , type = ' int ' , metavar = ' int ' ,
2014-01-20 20:11:56 +05:30
help = ' diffusion distance in voxels [ %d efault] ' )
2014-06-07 23:43:29 +05:30
parser . add_option ( ' -N ' , ' --smooth ' , dest = ' N ' , type = ' int ' , metavar = ' int ' ,
2013-07-01 22:45:24 +05:30
help = ' N for curvature flow [ %d efault] ' )
2014-06-07 23:43:29 +05:30
parser . add_option ( ' -r ' , ' --renumber ' , dest = ' renumber ' , action = ' store_true ' ,
2014-02-14 18:47:29 +05:30
help = ' renumber microstructure indices from 1...N [ %d efault] ' )
2014-09-12 19:44:55 +05:30
parser . add_option ( ' -i ' , ' --immutable ' , action = ' extend ' , dest = ' immutable ' , metavar = ' <LIST> ' ,
2014-06-07 23:43:29 +05:30
help = ' list of immutable microstructures ' )
2013-06-21 01:15:25 +05:30
2013-07-01 22:45:24 +05:30
parser . set_defaults ( d = 1 )
parser . set_defaults ( N = 1 )
2014-02-14 18:47:29 +05:30
parser . set_defaults ( renumber = False )
2014-06-07 23:43:29 +05:30
parser . set_defaults ( immutable = [ ] )
2013-06-21 01:15:25 +05:30
2014-06-07 23:43:29 +05:30
( options , filenames ) = parser . parse_args ( )
2014-01-20 20:11:56 +05:30
2014-06-07 23:43:29 +05:30
options . immutable = map ( int , options . immutable )
2013-06-22 02:47:03 +05:30
2013-06-21 01:15:25 +05:30
#--- setup file handles --------------------------------------------------------------------------
files = [ ]
if filenames == [ ] :
2013-07-01 22:45:24 +05:30
files . append ( { ' name ' : ' STDIN ' ,
' input ' : sys . stdin ,
' output ' : sys . stdout ,
' croak ' : sys . stderr ,
} )
2013-06-21 01:15:25 +05:30
else :
2013-07-01 22:45:24 +05:30
for name in filenames :
if os . path . exists ( name ) :
files . append ( { ' name ' : name ,
' input ' : open ( name ) ,
' output ' : open ( name + ' _tmp ' , ' w ' ) ,
' croak ' : sys . stdout ,
} )
2013-06-21 01:15:25 +05:30
2013-06-30 19:21:21 +05:30
#--- loop over input files ------------------------------------------------------------------------
2013-06-21 01:15:25 +05:30
for file in files :
2013-10-25 17:28:03 +05:30
if file [ ' name ' ] != ' STDIN ' : file [ ' croak ' ] . write ( ' \033 [1m ' + scriptName + ' \033 [0m: ' + file [ ' name ' ] + ' \n ' )
2013-07-18 18:58:54 +05:30
else : file [ ' croak ' ] . write ( ' \033 [1m ' + scriptName + ' \033 [0m \n ' )
2013-06-21 01:15:25 +05:30
2014-02-04 05:14:29 +05:30
theTable = damask . ASCIItable ( file [ ' input ' ] , file [ ' output ' ] , labels = False , buffered = False )
2013-06-30 19:21:21 +05:30
theTable . head_read ( )
2013-06-21 01:15:25 +05:30
2013-06-30 19:21:21 +05:30
#--- interpret header ----------------------------------------------------------------------------
2013-06-21 01:15:25 +05:30
info = {
2014-06-18 14:30:57 +05:30
' grid ' : np . zeros ( 3 , ' i ' ) ,
' size ' : np . zeros ( 3 , ' d ' ) ,
' origin ' : np . zeros ( 3 , ' d ' ) ,
2013-06-21 01:15:25 +05:30
' homogenization ' : 0 ,
2013-06-30 19:21:21 +05:30
' microstructures ' : 0 ,
}
newInfo = {
' microstructures ' : 0 ,
2013-06-21 01:15:25 +05:30
}
2013-06-30 19:21:21 +05:30
extra_header = [ ]
2013-06-21 01:15:25 +05:30
2013-06-30 19:21:21 +05:30
for header in theTable . info :
2013-06-21 01:15:25 +05:30
headitems = map ( str . lower , header . split ( ) )
2013-06-30 19:21:21 +05:30
if len ( headitems ) == 0 : continue
for synonym , alternatives in synonyms . iteritems ( ) :
if headitems [ 0 ] in alternatives : headitems [ 0 ] = synonym
2013-06-21 01:15:25 +05:30
if headitems [ 0 ] in mappings . keys ( ) :
if headitems [ 0 ] in identifiers . keys ( ) :
for i in xrange ( len ( identifiers [ headitems [ 0 ] ] ) ) :
info [ headitems [ 0 ] ] [ i ] = \
mappings [ headitems [ 0 ] ] ( headitems [ headitems . index ( identifiers [ headitems [ 0 ] ] [ i ] ) + 1 ] )
else :
info [ headitems [ 0 ] ] = mappings [ headitems [ 0 ] ] ( headitems [ 1 ] )
2013-06-30 19:21:21 +05:30
else :
extra_header . append ( header )
2013-06-21 01:15:25 +05:30
file [ ' croak ' ] . write ( ' grid a b c: %s \n ' % ( ' x ' . join ( map ( str , info [ ' grid ' ] ) ) ) + \
' size x y z: %s \n ' % ( ' x ' . join ( map ( str , info [ ' size ' ] ) ) ) + \
' origin x y z: %s \n ' % ( ' : ' . join ( map ( str , info [ ' origin ' ] ) ) ) + \
' homogenization: %i \n ' % info [ ' homogenization ' ] + \
' microstructures: %i \n ' % info [ ' microstructures ' ] )
2014-06-18 14:30:57 +05:30
if np . any ( info [ ' grid ' ] < 1 ) :
2013-06-21 01:15:25 +05:30
file [ ' croak ' ] . write ( ' invalid grid a b c. \n ' )
2013-06-30 19:21:21 +05:30
continue
2014-06-18 14:30:57 +05:30
if np . any ( info [ ' size ' ] < = 0.0 ) :
2013-06-21 01:15:25 +05:30
file [ ' croak ' ] . write ( ' invalid size x y z. \n ' )
2013-06-30 19:21:21 +05:30
continue
2013-06-21 01:15:25 +05:30
2013-06-30 19:21:21 +05:30
#--- read data ------------------------------------------------------------------------------------
2014-08-19 03:02:53 +05:30
microstructure = np . zeros ( np . prod ( info [ ' grid ' ] ) , ' i ' ) # 2D structures do not work
2013-06-21 01:15:25 +05:30
i = 0
2014-02-04 05:14:29 +05:30
while theTable . data_read ( ) : # read next data line of ASCII table
2013-06-30 19:21:21 +05:30
items = theTable . data
2013-06-22 02:47:03 +05:30
if len ( items ) > 2 :
if items [ 1 ] . lower ( ) == ' of ' : items = [ int ( items [ 2 ] ) ] * int ( items [ 0 ] )
elif items [ 1 ] . lower ( ) == ' to ' : items = xrange ( int ( items [ 0 ] ) , 1 + int ( items [ 2 ] ) )
2013-06-30 19:21:21 +05:30
else : items = map ( int , items )
else : items = map ( int , items )
2013-06-22 02:47:03 +05:30
2013-06-30 19:21:21 +05:30
s = len ( items )
microstructure [ i : i + s ] = items
i + = s
2013-06-21 01:15:25 +05:30
2013-11-12 22:34:36 +05:30
#--- reshape, if 2D make copy ---------------------------------------------------------------------
2014-06-07 23:43:29 +05:30
2014-06-18 14:30:57 +05:30
microstructure = np . tile ( microstructure . reshape ( info [ ' grid ' ] , order = ' F ' ) ,
np . where ( info [ ' grid ' ] == 1 , 2 , 1 ) ) # make one copy along dimensions with grid == 1
grid = np . array ( microstructure . shape )
2014-06-07 23:43:29 +05:30
#--- initialize support data -----------------------------------------------------------------------
2014-06-18 14:30:57 +05:30
periodic_microstructure = np . tile ( microstructure , ( 3 , 3 , 3 ) ) [ grid [ 0 ] / 2 : - grid [ 0 ] / 2 ,
2014-06-07 23:43:29 +05:30
grid [ 1 ] / 2 : - grid [ 1 ] / 2 ,
grid [ 2 ] / 2 : - grid [ 2 ] / 2 ] # periodically extend the microstructure
2014-06-18 14:30:57 +05:30
microstructure_original = np . copy ( microstructure ) # store a copy the initial microstructure to find locations of immutable indices
2014-06-07 23:43:29 +05:30
2014-06-18 14:30:57 +05:30
X , Y , Z = np . mgrid [ 0 : grid [ 0 ] , 0 : grid [ 1 ] , 0 : grid [ 2 ] ]
gauss = np . exp ( - ( X * X + Y * Y + Z * Z ) / ( 2.0 * options . d * options . d ) ) / math . pow ( 2.0 * np . pi * options . d * options . d , 1.5 )
2014-06-07 23:43:29 +05:30
gauss [ : , : , grid [ 2 ] / 2 : : ] = gauss [ : , : , round ( grid [ 2 ] / 2. ) - 1 : : - 1 ] # trying to cope with uneven (odd) grid size
gauss [ : , grid [ 1 ] / 2 : : , : ] = gauss [ : , round ( grid [ 1 ] / 2. ) - 1 : : - 1 , : ]
gauss [ grid [ 0 ] / 2 : : , : , : ] = gauss [ round ( grid [ 0 ] / 2. ) - 1 : : - 1 , : , : ]
2014-06-18 14:30:57 +05:30
gauss = np . fft . rfftn ( gauss )
2014-04-01 22:13:39 +05:30
interfacialEnergy = lambda A , B : ( A * B != 0 ) * ( A != B ) * 1.0
2014-06-07 23:43:29 +05:30
struc = ndimage . generate_binary_structure ( 3 , 1 ) # 3D von Neumann neighborhood
2013-07-01 22:45:24 +05:30
for smoothIter in xrange ( options . N ) :
2014-06-18 14:30:57 +05:30
boundary = np . zeros ( microstructure . shape )
2014-06-07 23:43:29 +05:30
for i in ( - 1 , 0 , 1 ) :
for j in ( - 1 , 0 , 1 ) :
for k in ( - 1 , 0 , 1 ) :
2014-06-18 14:30:57 +05:30
interfaceEnergy = np . maximum ( boundary ,
interfacialEnergy ( microstructure , np . roll ( np . roll ( np . roll (
2014-06-07 23:43:29 +05:30
microstructure , i , axis = 0 ) , j , axis = 1 ) , k , axis = 2 ) ) ) # assign interfacial energy to all voxels that have a differing neighbor (in Moore neighborhood)
2014-06-18 14:30:57 +05:30
periodic_interfaceEnergy = np . tile ( interfaceEnergy , ( 3 , 3 , 3 ) ) [ grid [ 0 ] / 2 : - grid [ 0 ] / 2 ,
2014-06-07 23:43:29 +05:30
grid [ 1 ] / 2 : - grid [ 1 ] / 2 ,
grid [ 2 ] / 2 : - grid [ 2 ] / 2 ] # periodically extend interfacial energy array by half a grid size in positive and negative directions
index = ndimage . morphology . distance_transform_edt ( periodic_interfaceEnergy == 0. , # transform bulk volume (i.e. where interfacial energy is zero)
return_distances = False ,
return_indices = True ) # want array index of nearest voxel on periodically extended boundary
2014-06-18 14:30:57 +05:30
# boundaryExt = boundaryExt[index[0].flatten(),index[1].flatten(),index[2].flatten()].reshape(boundaryExt.shape) # fill bulk with energy of nearest interface | question PE: what "flatten" for?
2014-06-07 23:43:29 +05:30
periodic_bulkEnergy = periodic_interfaceEnergy [ index [ 0 ] ,
index [ 1 ] ,
index [ 2 ] ] . reshape ( 2 * grid ) # fill bulk with energy of nearest interface
2014-06-18 14:30:57 +05:30
diffusedEnergy = np . fft . irfftn ( np . fft . rfftn ( np . where ( ndimage . morphology . binary_dilation ( interfaceEnergy > 0. ,
2014-06-07 23:43:29 +05:30
structure = struc ,
iterations = options . d / 2 + 1 ) , # fat boundary | question PE: why 2d - 1? I would argue for d/2 + 1 !!
periodic_bulkEnergy [ grid [ 0 ] / 2 : - grid [ 0 ] / 2 , # retain filled energy on fat boundary...
grid [ 1 ] / 2 : - grid [ 1 ] / 2 ,
grid [ 2 ] / 2 : - grid [ 2 ] / 2 ] , # ...and zero everywhere else
0. ) \
) * gauss )
2014-06-18 14:30:57 +05:30
periodic_diffusedEnergy = np . tile ( diffusedEnergy , ( 3 , 3 , 3 ) ) [ grid [ 0 ] / 2 : - grid [ 0 ] / 2 ,
2014-06-07 23:43:29 +05:30
grid [ 1 ] / 2 : - grid [ 1 ] / 2 ,
grid [ 2 ] / 2 : - grid [ 2 ] / 2 ] # periodically extend the smoothed bulk energy
index = ndimage . morphology . distance_transform_edt ( periodic_diffusedEnergy > = 0.5 , # transform voxels close to interface region | question PE: what motivates 1/2 (could be any small number, or)?
return_distances = False ,
return_indices = True ) # want index of closest bulk grain
microstructure = periodic_microstructure [ index [ 0 ] ,
index [ 1 ] ,
index [ 2 ] ] . reshape ( 2 * grid ) [ grid [ 0 ] / 2 : - grid [ 0 ] / 2 ,
grid [ 1 ] / 2 : - grid [ 1 ] / 2 ,
grid [ 2 ] / 2 : - grid [ 2 ] / 2 ] # extent grains into interface region
2014-06-18 14:30:57 +05:30
immutable = np . zeros ( microstructure . shape , dtype = bool )
2014-06-05 22:48:52 +05:30
for micro in options . immutable :
2014-06-18 14:30:57 +05:30
immutable + = np . logical_or ( microstructure == micro , microstructure_original == micro ) # find locations where immutable microstructures have been or are now
2014-06-05 22:48:52 +05:30
2014-06-18 14:30:57 +05:30
microstructure = np . where ( immutable , microstructure_original , microstructure ) # undo any changes involving immutable microstructures
2013-07-01 22:45:24 +05:30
2014-02-14 18:47:29 +05:30
# --- renumber to sequence 1...Ngrains if requested ------------------------------------------------
2014-06-18 14:30:57 +05:30
# http://stackoverflow.com/questions/10741346/np-frequency-counts-for-unique-values-in-an-array
2014-06-07 23:43:29 +05:30
2014-02-14 18:47:29 +05:30
if options . renumber :
2014-06-07 23:43:29 +05:30
newID = 0
2014-06-18 14:30:57 +05:30
for microstructureID , count in enumerate ( np . bincount ( microstructure . flatten ( ) ) ) :
2014-02-14 18:47:29 +05:30
if count != 0 :
2014-06-07 23:43:29 +05:30
newID + = 1
2014-06-18 14:30:57 +05:30
microstructure = np . where ( microstructure == microstructureID , newID , microstructure )
2014-06-07 23:43:29 +05:30
2014-02-14 18:47:29 +05:30
# --- assemble header -----------------------------------------------------------------------------
2013-11-12 22:34:36 +05:30
newInfo [ ' microstructures ' ] = microstructure [ 0 : info [ ' grid ' ] [ 0 ] , 0 : info [ ' grid ' ] [ 1 ] , 0 : info [ ' grid ' ] [ 2 ] ] . max ( )
2013-06-30 19:21:21 +05:30
#--- report ---------------------------------------------------------------------------------------
if ( newInfo [ ' microstructures ' ] != info [ ' microstructures ' ] ) :
file [ ' croak ' ] . write ( ' --> microstructures: %i \n ' % newInfo [ ' microstructures ' ] )
#--- write header ---------------------------------------------------------------------------------
theTable . labels_clear ( )
theTable . info_clear ( )
theTable . info_append ( extra_header + [
2014-01-20 20:11:56 +05:30
scriptID + ' ' + ' ' . join ( sys . argv [ 1 : ] ) ,
2013-06-30 19:21:21 +05:30
" grid \t a %i \t b %i \t c %i " % ( info [ ' grid ' ] [ 0 ] , info [ ' grid ' ] [ 1 ] , info [ ' grid ' ] [ 2 ] , ) ,
" size \t x %f \t y %f \t z %f " % ( info [ ' size ' ] [ 0 ] , info [ ' size ' ] [ 1 ] , info [ ' size ' ] [ 2 ] , ) ,
" origin \t x %f \t y %f \t z %f " % ( info [ ' origin ' ] [ 0 ] , info [ ' origin ' ] [ 1 ] , info [ ' origin ' ] [ 2 ] , ) ,
" homogenization \t %i " % info [ ' homogenization ' ] ,
" microstructures \t %i " % ( newInfo [ ' microstructures ' ] ) ,
] )
theTable . head_write ( )
2013-06-21 01:15:25 +05:30
# --- write microstructure information ------------------------------------------------------------
2013-07-01 22:45:24 +05:30
formatwidth = int ( math . floor ( math . log10 ( microstructure . max ( ) ) + 1 ) )
2014-06-18 14:30:57 +05:30
theTable . data = microstructure [ 0 : info [ ' grid ' ] [ 0 ] , 0 : info [ ' grid ' ] [ 1 ] , 0 : info [ ' grid ' ] [ 2 ] ] . reshape ( np . prod ( info [ ' grid ' ] ) , order = ' F ' ) . transpose ( ) # question PE: this assumes that only the Z dimension can be 1!
2013-12-17 13:46:29 +05:30
theTable . data_writeArray ( ' %% %i i ' % ( formatwidth ) , delimiter = ' ' )
2013-06-30 19:21:21 +05:30
2013-06-21 01:15:25 +05:30
#--- output finalization --------------------------------------------------------------------------
if file [ ' name ' ] != ' STDIN ' :
2014-06-18 14:30:57 +05:30
theTable . __IO__ [ ' in ' ] . close ( )
theTable . __IO__ [ ' out ' ] . close ( )
2013-06-21 01:15:25 +05:30
os . rename ( file [ ' name ' ] + ' _tmp ' , file [ ' name ' ] )