Tutorial

The microstructural analysis is a powerful, but underused tool of petrostructural analysis. Except acquirement of common statistical parameters, this technique can significantly improve understanding of processes of grain nucleation and grain growth, can bring insights on the role of surface energies or quantify duration of metamorphic and magmatic cooling events as long as appropriate thermodynamical data for studied mineral exist. This technique also allows systematic evaluation of degree of preferred orientations of grain boundaries in conjunction with their frequencies. This may help to better understand the mobility of grain boundaries and precipitations or removal of different mineral phases.

We introduce a new platform, object-oriented Python package PolyLX providing several core routines for data exchange, visualization and analysis of microstructural data, which can be run on any platform supported by Scientific Python environment.

Grains objects

To start working with PolyLX we need to import the polylx package. For our convinience, we can import PolyLX into actual namespace:

[1]:
from polylx import *

To read example data, we can use example method. Note that we create new Grains object, which store all imported Grain features.

[2]:
g = Grains.example()

To visualize grain objects from shape file, we can use plot method of Grains object:

[3]:
g.show()
_images/tutorial_9_0.png

or we can just select Grains by its class name:

[4]:
g['qtz'].show()
_images/tutorial_11_0.png

The dot notation is used to access individual properties commonly returning values for individual Grain as numpy array.

[5]:
g['qtz'].ar
[5]:
array([1.46370088, 3.55371458, 1.43641139, 1.26293055, 2.10676277,
       1.45200805, 1.98973326, 1.97308557, 2.13420187, 1.76682269,
       1.70083897, 1.38205897, 1.88811465, 1.59948827, 2.50452919,
       1.60296389, 1.4918233 , 2.15318719, 1.27665794, 1.38714959,
       1.67235338, 2.33179583, 1.30609967, 2.73148246, 1.02760669,
       1.33627299, 2.65451284, 1.29069569, 1.73051094, 1.25763409,
       1.90027316, 2.56110638, 1.78555385, 2.40926108, 2.26741705,
       1.71957235, 1.79168709, 1.04770164, 1.293186  , 1.29420065,
       1.48331817, 2.15510614, 2.21246419, 1.57101091, 2.01989715,
       1.1428675 , 2.02888455, 4.07405108, 1.47968881, 1.24770095,
       1.4750185 , 1.37946472, 1.49048108, 1.56668345, 1.43717521,
       1.59756777, 1.58948843, 2.12557437, 2.54316052, 1.98917177,
       1.29809155, 1.70022052, 1.40121941, 1.24674038, 1.50255058,
       1.42880415, 1.73447054, 2.3548111 , 1.52891827, 3.26773221,
       1.33011244, 2.26173396, 3.2151532 , 2.15638456, 1.61602624,
       1.13898611, 2.91625233, 1.94275485, 2.68487563, 1.12446842,
       1.48814907, 1.79425743, 1.19512385, 1.28301942, 1.39853133,
       1.59860483, 3.80709622, 1.75016693, 1.59940152, 1.43972155,
       1.09439109, 2.00023212, 1.87470191, 1.04157011, 1.48561371,
       1.14172901, 1.48211332, 1.52569202, 1.59357336, 1.58054224,
       1.86890813, 1.84729576, 1.45085424, 1.4400654 , 2.6284034 ,
       1.62077026, 1.35218688, 1.69040095, 1.2829313 , 2.7380623 ,
       1.55901231, 1.72569674, 1.18396915, 1.67864861, 2.40971617,
       2.08496427, 2.12907657, 1.20981316, 1.46045276, 1.55428179,
       4.73482949, 2.32570855, 1.95106722, 1.81174297, 4.08295286,
       2.04530043, 1.56215221, 1.42587721, 1.70016792, 1.78887212,
       2.17273986, 2.47995119, 4.59660941, 3.43961286, 3.04193405,
       2.91162332, 2.98790473, 2.55352686, 1.33076709, 7.09385883,
       1.91715238, 1.47161362, 2.39020581, 1.51938795, 1.87839843,
       1.9946499 , 2.27873759, 4.50321651, 5.78162231, 6.9806063 ,
       1.3177092 , 2.33701528, 1.86371784, 1.26166336, 1.28322623])

or we can collect any properties to pandas.DataFrame using df method:

[6]:
g.df('la', 'sa', 'lao', 'sao', 'area', 'length', 'ead', 'ar').head(10)
[6]:
la sa lao sao area length ead ar
fid
0 0.066027 0.045110 70.596636 160.596636 0.002286 0.186196 0.053956 1.463701
1 0.099033 0.057029 70.983857 160.983857 0.004409 0.258753 0.074922 1.736522
2 0.074248 0.020893 61.438248 151.438248 0.001123 0.175821 0.037813 3.553715
3 0.045232 0.031489 85.088587 175.088587 0.001005 0.134427 0.035779 1.436411
4 0.136445 0.108038 170.839835 80.839835 0.011489 0.398558 0.120948 1.262931
5 0.073578 0.044938 123.223347 33.223347 0.002471 0.201258 0.056090 1.637319
6 0.103567 0.065119 149.397514 59.397514 0.005213 0.283110 0.081474 1.590441
7 0.103189 0.077988 23.758847 113.758847 0.005951 0.318774 0.087048 1.323142
8 0.187049 0.036611 82.108720 172.108720 0.004407 0.404066 0.074904 5.109041
9 0.270513 0.128402 76.193288 166.193288 0.024576 0.729051 0.176894 2.106763
[7]:
g.df('ead').describe()
[7]:
ead
count 701.000000
mean 0.072812
std 0.056812
min 0.000350
25% 0.037140
50% 0.058338
75% 0.093503
max 0.638144

To visualize orientations of objects, we can use rose method:

[8]:
g.rose()
_images/tutorial_18_0.png

or for just single class:

[9]:
g['ksp'].rose()
_images/tutorial_20_0.png

To aggregate and summarize multiple properties by different functions according to defined classification (name by default) we can use agg method:

[10]:
g.agg(
    N=['name', 'count'],
    area=['area', 'sum'],
    mean_ead=['ead', 'mean'],
    mean_or=['lao', circular.mean]
)
[10]:
N area mean_ead mean_or
class
ksp 254 2.443733 0.089710 76.875488
pl 292 1.083516 0.060629 94.197847
qtz 155 1.166097 0.068071 74.320337

The groups method return Pandas GroupBy object which allows any pandas-style manipulation

[11]:
g.groups('ead', 'area', 'la', 'sa').describe().T
[11]:
class ksp pl qtz
ead count 2.540000e+02 292.000000 1.550000e+02
mean 8.970974e-02 0.060629 6.807125e-02
std 6.495077e-02 0.032438 7.054971e-02
min 6.641998e-04 0.001850 3.501464e-04
25% 4.133005e-02 0.038226 2.970151e-02
50% 7.403298e-02 0.053984 4.794577e-02
75% 1.191733e-01 0.077308 7.892656e-02
max 4.105520e-01 0.190210 6.381439e-01
area count 2.540000e+02 292.000000 1.550000e+02
mean 9.620995e-03 0.003711 7.523208e-03
std 1.548182e-02 0.004170 2.778736e-02
min 3.464873e-07 0.000003 9.629176e-08
25% 1.341681e-03 0.001148 6.930225e-04
50% 4.304819e-03 0.002289 1.805471e-03
75% 1.115444e-02 0.004694 4.892680e-03
max 1.323812e-01 0.028416 3.198359e-01
la count 2.540000e+02 292.000000 1.550000e+02
mean 1.295772e-01 0.086681 1.019395e-01
std 1.053259e-01 0.053220 1.366152e-01
min 1.013949e-03 0.006461 1.017291e-03
25% 5.439610e-02 0.050202 4.314167e-02
50% 9.871911e-02 0.072777 7.151284e-02
75% 1.793952e-01 0.106761 1.206513e-01
max 8.097226e-01 0.279398 1.437277e+00
sa count 2.540000e+02 292.000000 1.550000e+02
mean 7.545538e-02 0.049585 5.255111e-02
std 5.428555e-02 0.027663 4.632415e-02
min 3.648908e-04 0.000583 1.457310e-04
25% 3.370683e-02 0.031980 2.183093e-02
50% 6.643814e-02 0.043545 3.640605e-02
75% 1.021515e-01 0.063468 6.490102e-02
max 3.252086e-01 0.166726 3.035541e-01

The method classify could be used to define new classification, based on any property and using variety of rules, e.g. 'quantile':

[12]:
g.classify('ar', rule='quantile', k=6)
df = g.df('class', 'name', 'area')
df.head()
[12]:
class name area
fid
0 1.45-1.63 qtz 0.002286
1 1.63-1.89 pl 0.004409
2 2.36-12.16 qtz 0.001123
3 1.28-1.45 qtz 0.001005
4 1.02-1.28 qtz 0.011489

To summarize results for individual phases per class we can use pandas pivot table:

[13]:
pd.pivot_table(df,index=['class'], columns=['name'], aggfunc='sum')
[13]:
area
name ksp pl qtz
class
1.02-1.28 0.348379 0.158328 0.079026
1.28-1.45 0.366220 0.176028 0.106734
1.45-1.63 0.424773 0.163026 0.192027
1.63-1.89 0.325575 0.157087 0.067631
1.89-2.36 0.362103 0.226971 0.168503
2.36-12.16 0.616684 0.202075 0.552178

or we can directly plot it..

[14]:
pd.pivot_table(df,index=['class'], columns=['name'], aggfunc='sum').plot(kind='bar');
_images/tutorial_30_0.png
[15]:
g.classify('ar', rule='jenks', k=6)
[16]:
g.show()
_images/tutorial_32_0.png
[17]:
g[132].show()
_images/tutorial_33_0.png

Boundaries objects

The map of grain boundaries could be created from topologically correct grains using boundaries method:

[18]:
b = g.boundaries()
[19]:
b.show()
_images/tutorial_36_0.png
[20]:
df = b.groups('length').sum()
df['percents']= 100 * df['length'] / df['length'].sum()
df
[20]:
length percents
class
ksp-ksp 23.383974 21.384276
ksp-pl 38.592227 35.291983
ksp-qtz 17.920424 16.387946
pl-pl 11.302490 10.335949
pl-qtz 11.535006 10.548581
qtz-qtz 6.617133 6.051264
[21]:
b.rose(weights=b.length, scaled=False)
_images/tutorial_38_0.png
[ ]: