diff --git a/cpo-dynamics-orthotropic/index.html b/cpo-dynamics-orthotropic/index.html index d9f7c3b6..15582111 100644 --- a/cpo-dynamics-orthotropic/index.html +++ b/cpo-dynamics-orthotropic/index.html @@ -154,11 +154,7 @@

Problem

\\ b({\bf x},t,\theta,\phi)=\sum_{l=0}^{L}\sum_{m=-l}^{l}b_{l}^{m}({\bf x},t) Y_{l}^{m}(\theta,\phi) \quad\text{(distribution of slip directions)}, \] -

CPO evolution can be written as a matrix problem involving the (block) state vector

-
\[ -{\bf s} = \begin{bmatrix} {\bf s}_n \\ {\bf s}_b \end{bmatrix} \quad\text{(state vector)}, -\]
-

where

+

CPO evolution can be written as two independent matrix problems involving the CPO state vector fields

\[ {\bf s}_n = [n_0^0,n_2^{-2},n_2^{-1},n_2^{0},n_2^{1},n_2^{2},n_4^{-4},\cdots,n_4^{4},\cdots,n_L^{-L},\cdots,n_L^{L}]^{\mathrm{T}} \quad\text{($n$ state vector)}, \\ @@ -166,9 +162,11 @@

Problem

\]

such that

\[ -\frac{\mathrm{D}{\bf s}}{\mathrm{D} t} = {\bf M} \cdot {\bf s} \quad\text{(state evolution)}, +\frac{\mathrm{D}{\bf s}_n}{\mathrm{D} t} = {\bf M}_n \cdot {\bf s}_n \quad\text{($n$ state evolution)}, +\\ +\frac{\mathrm{D}{\bf s}_b}{\mathrm{D} t} = {\bf M}_b \cdot {\bf s}_b \quad\text{($b$ state evolution)}, \]
-

where the operator (matrix) \({\bf M}\) represents the effect of a given CPO process, which may depend on stress, strain-rate, temperature, etc.

+

where the operators (matrices) \({\bf M}_n\) and \({\bf M}_b\) represents the effect of a given CPO process, which may depend on stress, strain-rate, temperature, etc.

Note

The distributions may also be understood as the mass density fraction of grains with a given slip-plane-normal and slip-direction orientation. diff --git a/cpo-idealized/index.html b/cpo-idealized/index.html index 9f3815f9..98b27e38 100644 --- a/cpo-idealized/index.html +++ b/cpo-idealized/index.html @@ -126,15 +126,15 @@

Idealized CPOs

-

Three types of idealized CPO states can be said to exist:

+

If concerned with the distribution of a single crystallographic axis, three types of idealized CPO states can be said to exist:

-

Each of these can be expanded in terms of spherical harmonics by using the sifting property of the delta function, \(\delta({\hat {\bf r}})\).

+

Each of these can be expanded as a spherical harmonics series by using the sifting property of the delta function \(\delta({\hat {\bf r}})\).

Unidirectional

-

Consider the case where grains are perfectly aligned with \({{\bf m}}\) such that \(n({\hat {\bf r}}) = \delta({\hat {\bf r}}-{{\bf m}})\). +

Consider the case where slip-plane normals are perfectly aligned with \({{\bf m}}\) such that \(n({\hat {\bf r}}) = \delta({\hat {\bf r}}-{{\bf m}})\). The corresponding expansion coefficients follow from the usual overlap integral:

\[ n_l^m diff --git a/cpo-representation/index.html b/cpo-representation/index.html index 0b14c978..69235d11 100644 --- a/cpo-representation/index.html +++ b/cpo-representation/index.html @@ -176,8 +176,7 @@

CPO representation

Olivine

-

For orthotropic grains such as olivine, both \(n(\theta,\phi)\) and \(b(\theta,\phi)\) distributions must be tracked to represent the CPO. -Note that \(n(\theta,\phi)\) and \(b(\theta,\phi)\) represent the distributions of particular crystallographic axes (\({\bf m}'_i\)) depending on fabric type (A—E type).

+

For orthotropic grains such as olivine, both \(n(\theta,\phi)\) and \(b(\theta,\phi)\) distributions must be tracked to represent the CPO.

@@ -192,6 +191,7 @@

CPO representation

+

Note that \(n(\theta,\phi)\) and \(b(\theta,\phi)\) represent the distributions of particular crystallographic axes (\({\bf m}'_i\)) depending on fabric type (A—E type).

ODF

The orientation distribution function (ODF) of a given slip-system axis (crystallographic axis) \(f\in \lbrace n,b\rbrace\) is defined as the normalized distribution

diff --git a/index.html b/index.html index 253ebcf4..7d32f153 100644 --- a/index.html +++ b/index.html @@ -252,5 +252,5 @@

Initialize

diff --git a/search/search_index.json b/search/search_index.json index 417eee3f..28c1d6c5 100644 --- a/search/search_index.json +++ b/search/search_index.json @@ -1 +1 @@ -{"config":{"indexing":"full","lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"","text":"specfab documentation Spectral CPO model of polycrystalline materials that can: Model lattice rotation, discontinuous DRX, and rotation/continuous DRX. Calculate CPO-induced viscous anisotropies using Sachs/Taylor homogenizations. Calculate elastic P- and S-wave velocities using Voigt/Reuss homogenizations. Provide expressions for forward+inverse orthotropic and transversely isotropic rheologies. Convert between structure tensors and spectral expansions coefficients. Be integrated with finite-element codes such as Elmer and FEniCS. By Nicholas M. Rathmann and David A. Lilien Glacier ice demo Install Source code is available here . Environment How to install Python in Linux - PyPI package: pip3 install numpy --upgrade && pip3 install specfabpy - Compile yourself: cd src && make python Python in Windows/Mac You will have to compile specfab yourself. Fortran Run cd src && make specfab.o Elmer/Ice Interface Compile shared library by running cd src && make libspecfab.so Libraries required: BLAS, LAPACK Initialize Initialize specfab by running import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(10) # L=10 truncation is sufficient for many cases nlm = np.zeros(nlm_len, dtype=np.complex64) # vector of harmonic expansion coefficients nlm[0] = 1/np.sqrt(4*np.pi) # normalized isotropic distribution where Variable Interpretation nlm_len Number of expansion coefficients for expansion series truncated at \\(l=L\\) nlm Vector of complex-valued expansion coefficients (state vector) lm Vector of degree and order integers ( l , m ) associated with each entry in nlm","title":"About"},{"location":"#specfab-documentation","text":"Spectral CPO model of polycrystalline materials that can: Model lattice rotation, discontinuous DRX, and rotation/continuous DRX. Calculate CPO-induced viscous anisotropies using Sachs/Taylor homogenizations. Calculate elastic P- and S-wave velocities using Voigt/Reuss homogenizations. Provide expressions for forward+inverse orthotropic and transversely isotropic rheologies. Convert between structure tensors and spectral expansions coefficients. Be integrated with finite-element codes such as Elmer and FEniCS. By Nicholas M. Rathmann and David A. Lilien","title":"specfab documentation"},{"location":"#glacier-ice-demo","text":"","title":"Glacier ice demo"},{"location":"#install","text":"Source code is available here . Environment How to install Python in Linux - PyPI package: pip3 install numpy --upgrade && pip3 install specfabpy - Compile yourself: cd src && make python Python in Windows/Mac You will have to compile specfab yourself. Fortran Run cd src && make specfab.o Elmer/Ice Interface Compile shared library by running cd src && make libspecfab.so Libraries required: BLAS, LAPACK","title":"Install"},{"location":"#initialize","text":"Initialize specfab by running import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(10) # L=10 truncation is sufficient for many cases nlm = np.zeros(nlm_len, dtype=np.complex64) # vector of harmonic expansion coefficients nlm[0] = 1/np.sqrt(4*np.pi) # normalized isotropic distribution where Variable Interpretation nlm_len Number of expansion coefficients for expansion series truncated at \\(l=L\\) nlm Vector of complex-valued expansion coefficients (state vector) lm Vector of degree and order integers ( l , m ) associated with each entry in nlm","title":"Initialize"},{"location":"FEM-integration/","text":"Finite element integration The coupled problem The coupled evolution of flow and CPO can be solved by joining different components of specfab together with a Stokes flow solver (purple piece): That is, specfab can model/provides CPO evolution given the local large-scale stress, strain-rate and temperature (green piece) Viscous anisotropies induced by the local CPO (orange piece) Bulk anisotropic rheologies given the local viscous anisotropy (blue piece) Elmer/Ice See Lilien et al. (2023) for ice-flow modelling. FEniCS To be updated.","title":"FEM integration"},{"location":"FEM-integration/#finite-element-integration","text":"","title":"Finite element integration"},{"location":"FEM-integration/#the-coupled-problem","text":"The coupled evolution of flow and CPO can be solved by joining different components of specfab together with a Stokes flow solver (purple piece): That is, specfab can model/provides CPO evolution given the local large-scale stress, strain-rate and temperature (green piece) Viscous anisotropies induced by the local CPO (orange piece) Bulk anisotropic rheologies given the local viscous anisotropy (blue piece)","title":"The coupled problem"},{"location":"FEM-integration/#elmerice","text":"See Lilien et al. (2023) for ice-flow modelling.","title":"Elmer/Ice"},{"location":"FEM-integration/#fenics","text":"To be updated.","title":"FEniCS"},{"location":"constitutive-elastic/","text":"Elastic constitutive equations Linear elastic constituve equations are supported in both forward and inverse (or reverse) form. Transversely isotropic Symmetries Stiffness matrix \\({\\bf C}\\) for \\(\\bf{m}=\\hat{\\bf{z}}\\) \\(\\begin{bmatrix}\\gamma & \\lambda & \\hat{\\lambda}\\lambda & 0 & 0 & 0 \\\\\\lambda & \\gamma & \\hat{\\lambda}\\lambda & 0 & 0 & 0 \\\\\\hat{\\lambda}\\lambda & \\hat{\\lambda}\\lambda & \\hat{\\gamma}\\gamma & 0 & 0 & 0 \\\\0&0&0& \\hat{\\mu}\\mu & 0 & 0\\\\0&0&0& 0 & \\hat{\\mu}\\mu & 0\\\\0&0&0& 0 & 0 & \\mu\\\\\\end{bmatrix}\\) API E = sf.elas_fwd_tranisotropic(S, lam, mu, Elam, Emu, Egam, m) S = sf.elas_rev_tranisotropic(E, lam, mu, Elam, Emu, Egam, m) where Arguments Type S , E Stress and strain tensor (3x3) lam , mu Isotropic Lam\u00e9 parameters \\(\\lambda\\) and \\(\\mu\\) Elam , Emu , Egam Anisotropic enhancement factors \\(\\hat{\\lambda}\\) , \\(\\hat{\\mu}\\) , and \\(\\hat{\\gamma}\\) m Rotational symmetry axis \\(\\bf{m}\\) Note P-wave modulus is not a free parameter but given by \\(\\gamma \\equiv \\lambda + 2\\mu\\) . Tip: convert from \\(C_{ij}\\) to Lam\u00e9 parameters Cij = (C11,C33,C55, C12,C13) (lam,mu, Elam,Emu,Egam) = sf.Cij_to_Lame_tranisotropic(Cij) Orthotropic Symmetries Stiffness matrix \\({\\bf C}\\) for \\(({\\bf m}_1,{\\bf m}_2,{\\bf m}_3)=(\\hat{{\\bf x}},\\hat{{\\bf y}},\\hat{{\\bf z}})\\) \\(\\small\\begin{bmatrix} \\lambda_{11} + 2\\mu_1 & \\lambda_{12} & \\lambda_{13} & 0 & 0 & 0 \\\\ \\lambda_{12} & \\lambda_{22} + 2\\mu_2 & \\lambda_{23} & 0 & 0 & 0 \\\\ \\lambda_{13 }& \\lambda_{23} & \\lambda_{33} + 2\\mu_3 & 0 & 0 & 0 \\\\ 0 & 0 & 0 & \\dfrac{\\mu_2+\\mu_3}{2} & 0 & 0 \\\\ 0 & 0 & 0 & 0 & \\dfrac{\\mu_3+\\mu_1}{2} & 0 \\\\ 0 & 0 & 0 & 0 & 0 & \\dfrac{\\mu_1+\\mu_2}{2} \\end{bmatrix}\\) API Not yet made available E = sf.elas_fwd_orthotropic(S, lame, m1,m2,m3) S = sf.elas_rev_orthotropic(E, lame, m1,m2,m3) where Arguments Type S , E Stress and strain tensor (3x3) lame Tuple of anisotropic Lam\u00e9 parameters \\((\\lambda_{11},\\lambda_{22},\\lambda_{33}, \\lambda_{23},\\lambda_{13},\\lambda_{12}, \\mu_{1}, \\mu_{2}, \\mu_{3})\\) m1 , m2 , m3 Reflection symmetry axes \\(\\bf{m}_1\\) , \\(\\bf{m}_2\\) , and \\(\\bf{m}_3\\) Tip: convert from \\(C_{ij}\\) to Lam\u00e9 parameters Cij = (C11,C22,C33,C44,C55,C66, C23,C13,C12) (lam11,lam22,lam33, lam23,lam13,lam12, mu1,mu2,mu3) = sf.Cij_to_Lame_orthotropic(Cij)","title":"Constitutive equations"},{"location":"constitutive-elastic/#elastic-constitutive-equations","text":"Linear elastic constituve equations are supported in both forward and inverse (or reverse) form.","title":"Elastic constitutive equations"},{"location":"constitutive-elastic/#transversely-isotropic","text":"Symmetries Stiffness matrix \\({\\bf C}\\) for \\(\\bf{m}=\\hat{\\bf{z}}\\) \\(\\begin{bmatrix}\\gamma & \\lambda & \\hat{\\lambda}\\lambda & 0 & 0 & 0 \\\\\\lambda & \\gamma & \\hat{\\lambda}\\lambda & 0 & 0 & 0 \\\\\\hat{\\lambda}\\lambda & \\hat{\\lambda}\\lambda & \\hat{\\gamma}\\gamma & 0 & 0 & 0 \\\\0&0&0& \\hat{\\mu}\\mu & 0 & 0\\\\0&0&0& 0 & \\hat{\\mu}\\mu & 0\\\\0&0&0& 0 & 0 & \\mu\\\\\\end{bmatrix}\\)","title":"Transversely isotropic"},{"location":"constitutive-elastic/#api","text":"","title":"API"},{"location":"constitutive-elastic/#e-sfelas_fwd_tranisotropics-lam-mu-elam-emu-egam-m","text":"","title":"E = sf.elas_fwd_tranisotropic(S, lam, mu, Elam, Emu, Egam, m)"},{"location":"constitutive-elastic/#s-sfelas_rev_tranisotropice-lam-mu-elam-emu-egam-m","text":"where Arguments Type S , E Stress and strain tensor (3x3) lam , mu Isotropic Lam\u00e9 parameters \\(\\lambda\\) and \\(\\mu\\) Elam , Emu , Egam Anisotropic enhancement factors \\(\\hat{\\lambda}\\) , \\(\\hat{\\mu}\\) , and \\(\\hat{\\gamma}\\) m Rotational symmetry axis \\(\\bf{m}\\) Note P-wave modulus is not a free parameter but given by \\(\\gamma \\equiv \\lambda + 2\\mu\\) . Tip: convert from \\(C_{ij}\\) to Lam\u00e9 parameters Cij = (C11,C33,C55, C12,C13) (lam,mu, Elam,Emu,Egam) = sf.Cij_to_Lame_tranisotropic(Cij)","title":"S = sf.elas_rev_tranisotropic(E, lam, mu, Elam, Emu, Egam, m)"},{"location":"constitutive-elastic/#orthotropic","text":"Symmetries Stiffness matrix \\({\\bf C}\\) for \\(({\\bf m}_1,{\\bf m}_2,{\\bf m}_3)=(\\hat{{\\bf x}},\\hat{{\\bf y}},\\hat{{\\bf z}})\\) \\(\\small\\begin{bmatrix} \\lambda_{11} + 2\\mu_1 & \\lambda_{12} & \\lambda_{13} & 0 & 0 & 0 \\\\ \\lambda_{12} & \\lambda_{22} + 2\\mu_2 & \\lambda_{23} & 0 & 0 & 0 \\\\ \\lambda_{13 }& \\lambda_{23} & \\lambda_{33} + 2\\mu_3 & 0 & 0 & 0 \\\\ 0 & 0 & 0 & \\dfrac{\\mu_2+\\mu_3}{2} & 0 & 0 \\\\ 0 & 0 & 0 & 0 & \\dfrac{\\mu_3+\\mu_1}{2} & 0 \\\\ 0 & 0 & 0 & 0 & 0 & \\dfrac{\\mu_1+\\mu_2}{2} \\end{bmatrix}\\)","title":"Orthotropic"},{"location":"constitutive-elastic/#api_1","text":"Not yet made available","title":"API"},{"location":"constitutive-elastic/#e-sfelas_fwd_orthotropics-lame-m1m2m3","text":"","title":"E = sf.elas_fwd_orthotropic(S, lame, m1,m2,m3)"},{"location":"constitutive-elastic/#s-sfelas_rev_orthotropice-lame-m1m2m3","text":"where Arguments Type S , E Stress and strain tensor (3x3) lame Tuple of anisotropic Lam\u00e9 parameters \\((\\lambda_{11},\\lambda_{22},\\lambda_{33}, \\lambda_{23},\\lambda_{13},\\lambda_{12}, \\mu_{1}, \\mu_{2}, \\mu_{3})\\) m1 , m2 , m3 Reflection symmetry axes \\(\\bf{m}_1\\) , \\(\\bf{m}_2\\) , and \\(\\bf{m}_3\\) Tip: convert from \\(C_{ij}\\) to Lam\u00e9 parameters Cij = (C11,C22,C33,C44,C55,C66, C23,C13,C12) (lam11,lam22,lam33, lam23,lam13,lam12, mu1,mu2,mu3) = sf.Cij_to_Lame_orthotropic(Cij)","title":"S = sf.elas_rev_orthotropic(E, lame, m1,m2,m3)"},{"location":"constitutive-viscoplastic/","text":"Viscoplastic constitutive equations Anisotropic power-law rheologies are supported in both forward and inverse (or reverse) form. Eigenenhancements Bulk viscous anisotropy is prescribed in terms of logitudinal and shear strain-rate enhancement factors w.r.t rheological symmetry axes, termed eigenenhancements ( \\(E_{ij}\\) ). Transversely isotropic Rheolgical symmetries Forward rheology $$ {\\bf D} = \\eta^{-1} \\Big( {\\bf S} - \\lambda_1 ({\\bf S}:{\\bf M}){\\bf I} + \\lambda_2 ({\\bf S}:{\\bf M}){\\bf M} + \\lambda_3 ({\\bf S}\\cdot{\\bf M} + {\\bf M}\\cdot{\\bf S}) \\Big) $$ $$ \\eta^{-1} = A\\Big( {\\bf S}:{\\bf S} + \\lambda_2 ({\\bf S}:{\\bf M})^2 + 2\\lambda_2 ({\\bf S}^2:{\\bf M}) \\Big)^{(n-1)/2} $$ $$ {\\bf M}={\\bf m}^2$$ where the material parameters \\(\\lambda_i\\) depend on the eigenenhancements: \\[ \\lambda_1 = \\frac{E_{mm}^{2/(n+1)}-1}{2} ,\\quad \\lambda_2 = \\frac{3(E_{mm}^{2/(n+1)}-1) - 4(E_{mt}^{2/(n+1)}-1)}{2} ,\\quad \\lambda_3 = E_{mt}^{2/(n+1)}-1 \\] API D = sf.rheo_fwd_tranisotropic(S, A, n, m, Eij) S = sf.rheo_rev_tranisotropic(D, A, n, m, Eij) where Arguments Type S , D Deviatoric-stress and strain-rate tensor (3x3) A , n Flow-rate factor \\(A\\) and power-law exponent \\(n\\) m Rotational symmetry axis \\(\\bf{m}\\) Eij Tuple of eigenenhancements (Emm, Emt) Orthotropic Rheolgical symmetries Forward rheology \\({\\bf D} = \\eta^{-1} \\displaystyle\\sum_{i=1}^{3} \\Big[ \\lambda_i ({\\bf S}:{\\bf M}_i){\\bf M}_{i} + \\lambda_{i+3} ({\\bf S}:{\\bf M}_{i+3}) {\\bf M}_{i+3} \\Big]\\) \\(\\eta^{-1} = A\\left( \\displaystyle\\sum_{i=1}^3 \\Big[ \\lambda_i ({\\bf S}:{\\bf M}_{i})^2 + \\lambda_{i+3} ({\\bf S}:{\\bf M}_{i+3})^2 \\Big] \\right)^{(n-1)/2}\\) \\({\\bf M}_{i} = \\dfrac{{\\bf m}_{j_i} {\\bf m}_{j_i} - {\\bf m}_{k_i} {\\bf m}_{k_i}}{2} ,\\quad {\\bf M}_{i+3} = \\dfrac{{\\bf m}_{j_i} {\\bf m}_{k_i} + {\\bf m}_{k_i} {\\bf m}_{j_i}}{2},\\) \\((j_1, j_2, j_3) = (2,3,1),\\quad (k_1, k_2, k_3) = (3,1,2)\\) where the material parameters \\(\\lambda_i\\) depend on the eigenenhancements: \\[ \\lambda_i = \\frac{4}{3} \\left( E_{j_i j_i}^{2/(n+1)} + E_{k_i k_i}^{2/(n+1)} - E_{i i}^{2/(n+1)} \\right),\\quad \\lambda_{i+3} = 2 E_{j_i k_i}^{2/(n+1)} \\] API D = sf.rheo_fwd_orthotropic(S, A, n, m1,m2,m3, Eij) S = sf.rheo_rev_orthotropic(D, A, n, m1,m2,m3, Eij) where Arguments Type S , D Deviatoric-stress and strain-rate tensor (3x3) A , n Flow-rate factor \\(A\\) and power-law exponent \\(n\\) m1 , m2 , m3 Reflection symmetry axes \\(\\bf{m}_1\\) , \\(\\bf{m}_2\\) , and \\(\\bf{m}_3\\) Eij Tuple of eigenenhancements (E11,E22,E33,E23,E13,E12)","title":"Constitutive equations"},{"location":"constitutive-viscoplastic/#viscoplastic-constitutive-equations","text":"Anisotropic power-law rheologies are supported in both forward and inverse (or reverse) form. Eigenenhancements Bulk viscous anisotropy is prescribed in terms of logitudinal and shear strain-rate enhancement factors w.r.t rheological symmetry axes, termed eigenenhancements ( \\(E_{ij}\\) ).","title":"Viscoplastic constitutive equations"},{"location":"constitutive-viscoplastic/#transversely-isotropic","text":"Rheolgical symmetries Forward rheology $$ {\\bf D} = \\eta^{-1} \\Big( {\\bf S} - \\lambda_1 ({\\bf S}:{\\bf M}){\\bf I} + \\lambda_2 ({\\bf S}:{\\bf M}){\\bf M} + \\lambda_3 ({\\bf S}\\cdot{\\bf M} + {\\bf M}\\cdot{\\bf S}) \\Big) $$ $$ \\eta^{-1} = A\\Big( {\\bf S}:{\\bf S} + \\lambda_2 ({\\bf S}:{\\bf M})^2 + 2\\lambda_2 ({\\bf S}^2:{\\bf M}) \\Big)^{(n-1)/2} $$ $$ {\\bf M}={\\bf m}^2$$ where the material parameters \\(\\lambda_i\\) depend on the eigenenhancements: \\[ \\lambda_1 = \\frac{E_{mm}^{2/(n+1)}-1}{2} ,\\quad \\lambda_2 = \\frac{3(E_{mm}^{2/(n+1)}-1) - 4(E_{mt}^{2/(n+1)}-1)}{2} ,\\quad \\lambda_3 = E_{mt}^{2/(n+1)}-1 \\]","title":"Transversely isotropic"},{"location":"constitutive-viscoplastic/#api","text":"","title":"API"},{"location":"constitutive-viscoplastic/#d-sfrheo_fwd_tranisotropics-a-n-m-eij","text":"","title":"D = sf.rheo_fwd_tranisotropic(S, A, n, m, Eij)"},{"location":"constitutive-viscoplastic/#s-sfrheo_rev_tranisotropicd-a-n-m-eij","text":"where Arguments Type S , D Deviatoric-stress and strain-rate tensor (3x3) A , n Flow-rate factor \\(A\\) and power-law exponent \\(n\\) m Rotational symmetry axis \\(\\bf{m}\\) Eij Tuple of eigenenhancements (Emm, Emt)","title":"S = sf.rheo_rev_tranisotropic(D, A, n, m, Eij)"},{"location":"constitutive-viscoplastic/#orthotropic","text":"Rheolgical symmetries Forward rheology \\({\\bf D} = \\eta^{-1} \\displaystyle\\sum_{i=1}^{3} \\Big[ \\lambda_i ({\\bf S}:{\\bf M}_i){\\bf M}_{i} + \\lambda_{i+3} ({\\bf S}:{\\bf M}_{i+3}) {\\bf M}_{i+3} \\Big]\\) \\(\\eta^{-1} = A\\left( \\displaystyle\\sum_{i=1}^3 \\Big[ \\lambda_i ({\\bf S}:{\\bf M}_{i})^2 + \\lambda_{i+3} ({\\bf S}:{\\bf M}_{i+3})^2 \\Big] \\right)^{(n-1)/2}\\) \\({\\bf M}_{i} = \\dfrac{{\\bf m}_{j_i} {\\bf m}_{j_i} - {\\bf m}_{k_i} {\\bf m}_{k_i}}{2} ,\\quad {\\bf M}_{i+3} = \\dfrac{{\\bf m}_{j_i} {\\bf m}_{k_i} + {\\bf m}_{k_i} {\\bf m}_{j_i}}{2},\\) \\((j_1, j_2, j_3) = (2,3,1),\\quad (k_1, k_2, k_3) = (3,1,2)\\) where the material parameters \\(\\lambda_i\\) depend on the eigenenhancements: \\[ \\lambda_i = \\frac{4}{3} \\left( E_{j_i j_i}^{2/(n+1)} + E_{k_i k_i}^{2/(n+1)} - E_{i i}^{2/(n+1)} \\right),\\quad \\lambda_{i+3} = 2 E_{j_i k_i}^{2/(n+1)} \\]","title":"Orthotropic"},{"location":"constitutive-viscoplastic/#api_1","text":"","title":"API"},{"location":"constitutive-viscoplastic/#d-sfrheo_fwd_orthotropics-a-n-m1m2m3-eij","text":"","title":"D = sf.rheo_fwd_orthotropic(S, A, n, m1,m2,m3, Eij)"},{"location":"constitutive-viscoplastic/#s-sfrheo_rev_orthotropicd-a-n-m1m2m3-eij","text":"where Arguments Type S , D Deviatoric-stress and strain-rate tensor (3x3) A , n Flow-rate factor \\(A\\) and power-law exponent \\(n\\) m1 , m2 , m3 Reflection symmetry axes \\(\\bf{m}_1\\) , \\(\\bf{m}_2\\) , and \\(\\bf{m}_3\\) Eij Tuple of eigenenhancements (E11,E22,E33,E23,E13,E12)","title":"S = sf.rheo_rev_orthotropic(D, A, n, m1,m2,m3, Eij)"},{"location":"cpo-dynamics-orthotropic/","text":"CPO dynamics for orthotropic grains This tutorial focuses on modelling the CPO evolution of polycrystalline olivine. That is, the distribution of (easy) slip-plane normals and slip directions of grains, \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) . Polycrystalline olivine Ensemble of slip elements The distributions \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) refer to certain crystallographic axes ( \\({\\bf m}'_i\\) ) depending on the fabric type; i.e. thermodynamic conditions, water content, and stress magnitude that control which of the crystallographic slip systems is activated. Problem Given the expansions \\[ n({\\bf x},t,\\theta,\\phi)=\\sum_{l=0}^{L}\\sum_{m=-l}^{l}n_{l}^{m}({\\bf x},t) Y_{l}^{m}(\\theta,\\phi) \\quad\\text{(distribution of slip-plane normals)}, \\\\ b({\\bf x},t,\\theta,\\phi)=\\sum_{l=0}^{L}\\sum_{m=-l}^{l}b_{l}^{m}({\\bf x},t) Y_{l}^{m}(\\theta,\\phi) \\quad\\text{(distribution of slip directions)}, \\] CPO evolution can be written as a matrix problem involving the (block) state vector \\[ {\\bf s} = \\begin{bmatrix} {\\bf s}_n \\\\ {\\bf s}_b \\end{bmatrix} \\quad\\text{(state vector)}, \\] where \\[ {\\bf s}_n = [n_0^0,n_2^{-2},n_2^{-1},n_2^{0},n_2^{1},n_2^{2},n_4^{-4},\\cdots,n_4^{4},\\cdots,n_L^{-L},\\cdots,n_L^{L}]^{\\mathrm{T}} \\quad\\text{($n$ state vector)}, \\\\ {\\bf s}_b = [b_0^0,b_2^{-2},b_2^{-1},b_2^{0},b_2^{1},b_2^{2},b_4^{-4},\\cdots,b_4^{4},\\cdots,b_L^{-L},\\cdots,b_L^{L}]^{\\mathrm{T}} \\quad\\text{($b$ state vector)}, \\] such that \\[ \\frac{\\mathrm{D}{\\bf s}}{\\mathrm{D} t} = {\\bf M} \\cdot {\\bf s} \\quad\\text{(state evolution)}, \\] where the operator (matrix) \\({\\bf M}\\) represents the effect of a given CPO process, which may depend on stress, strain-rate, temperature, etc. Note The distributions may also be understood as the mass density fraction of grains with a given slip-plane-normal and slip-direction orientation. See CPO representation for details. Lagrangian parcel The tutorial shows how to model the CPO evolution of a Lagrangian material parcel subject to three different modes of deformation : Lattice rotation To be published before documented here. Regularization Same as CPO dynamics for transversely isotropic grains","title":"Orthotropic grains"},{"location":"cpo-dynamics-orthotropic/#cpo-dynamics-for-orthotropic-grains","text":"This tutorial focuses on modelling the CPO evolution of polycrystalline olivine. That is, the distribution of (easy) slip-plane normals and slip directions of grains, \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) . Polycrystalline olivine Ensemble of slip elements The distributions \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) refer to certain crystallographic axes ( \\({\\bf m}'_i\\) ) depending on the fabric type; i.e. thermodynamic conditions, water content, and stress magnitude that control which of the crystallographic slip systems is activated.","title":"CPO dynamics for orthotropic grains"},{"location":"cpo-dynamics-orthotropic/#problem","text":"Given the expansions \\[ n({\\bf x},t,\\theta,\\phi)=\\sum_{l=0}^{L}\\sum_{m=-l}^{l}n_{l}^{m}({\\bf x},t) Y_{l}^{m}(\\theta,\\phi) \\quad\\text{(distribution of slip-plane normals)}, \\\\ b({\\bf x},t,\\theta,\\phi)=\\sum_{l=0}^{L}\\sum_{m=-l}^{l}b_{l}^{m}({\\bf x},t) Y_{l}^{m}(\\theta,\\phi) \\quad\\text{(distribution of slip directions)}, \\] CPO evolution can be written as a matrix problem involving the (block) state vector \\[ {\\bf s} = \\begin{bmatrix} {\\bf s}_n \\\\ {\\bf s}_b \\end{bmatrix} \\quad\\text{(state vector)}, \\] where \\[ {\\bf s}_n = [n_0^0,n_2^{-2},n_2^{-1},n_2^{0},n_2^{1},n_2^{2},n_4^{-4},\\cdots,n_4^{4},\\cdots,n_L^{-L},\\cdots,n_L^{L}]^{\\mathrm{T}} \\quad\\text{($n$ state vector)}, \\\\ {\\bf s}_b = [b_0^0,b_2^{-2},b_2^{-1},b_2^{0},b_2^{1},b_2^{2},b_4^{-4},\\cdots,b_4^{4},\\cdots,b_L^{-L},\\cdots,b_L^{L}]^{\\mathrm{T}} \\quad\\text{($b$ state vector)}, \\] such that \\[ \\frac{\\mathrm{D}{\\bf s}}{\\mathrm{D} t} = {\\bf M} \\cdot {\\bf s} \\quad\\text{(state evolution)}, \\] where the operator (matrix) \\({\\bf M}\\) represents the effect of a given CPO process, which may depend on stress, strain-rate, temperature, etc. Note The distributions may also be understood as the mass density fraction of grains with a given slip-plane-normal and slip-direction orientation. See CPO representation for details.","title":"Problem"},{"location":"cpo-dynamics-orthotropic/#lagrangian-parcel","text":"The tutorial shows how to model the CPO evolution of a Lagrangian material parcel subject to three different modes of deformation :","title":"Lagrangian parcel"},{"location":"cpo-dynamics-orthotropic/#lattice-rotation","text":"To be published before documented here.","title":"Lattice rotation"},{"location":"cpo-dynamics-orthotropic/#regularization","text":"Same as CPO dynamics for transversely isotropic grains","title":"Regularization"},{"location":"cpo-dynamics-tranisotropic/","text":"CPO dynamics for transversely isotropic grains This tutorial focuses on modelling the CPO evolution of polycrystalline glacier ice. That is, the distribution \\(n(\\theta,\\phi)\\) of (easy) slip-plane normals of grains ( \\({\\bf n}={\\bf c}\\) ). Polycrystalline ice Ensemble of slip elements Problem Given the expansion \\[ n({\\bf x},t,\\theta,\\phi)=\\sum_{l=0}^{L}\\sum_{m=-l}^{l}n_{l}^{m}({\\bf x},t) Y_{l}^{m}(\\theta,\\phi) \\quad\\text{(distribution of slip-plane normals)}, \\] CPO evolution can be written as a matrix problem involving the state vector \\[ {\\bf s} = [n_0^0,n_2^{-2},n_2^{-1},n_2^{0},n_2^{1},n_2^{2},n_4^{-4},\\cdots,n_4^{4},\\cdots,n_L^{-L},\\cdots,n_L^{L}]^{\\mathrm{T}} \\quad\\text{(state vector)}, \\] such that \\[ \\frac{\\mathrm{D}{\\bf s}}{\\mathrm{D} t} = {\\bf M} \\cdot {\\bf s} \\quad\\text{(state evolution)}, \\] where the operator (matrix) \\({\\bf M}\\) represents the effect of a given CPO process, which may depend on stress, strain-rate, temperature, etc. The total effect of multiple processes acting simultaneously is simply \\[ {\\bf M} = {\\bf M_{\\mathrm{LROT}}} + {\\bf M_{\\mathrm{DDRX}}} + {\\bf M_{\\mathrm{CDRX}}} + \\cdots \\quad\\text{(operator)}. \\] Note \\(n(\\theta,\\phi)\\) may also be understood as the mass density fraction of grains with a given slip-plane normal orientation. See CPO representation for details. Lagrangian parcel The tutorial shows how to model the CPO evolution of a Lagrangian material parcel subject to three different modes of deformation : Lattice rotation The strain-induced rotation of \\(c\\) -axes is modelled given the (local) velocity gradient tensor \\(\\nabla {\\bf u}\\) ( Svendsen and Hutter, 1996 ). The model is a kinematic model in the sense that \\(c\\) -axes rotate in response to the bulk rate of stretching, \\({\\bf D}\\) , and spin, \\({\\bf W}\\) , thereby allowing the detailed microscopic stress and strain rate fields to be neglected and hence interactions between neighboring grains to be disregarded. The modelled \\(c\\) -axes are taken to rotate with the bulk continuum spin ( \\({\\bf W}\\) ), plus some plastic spin correction ( \\({\\bf W}_{\\mathrm{p}}\\) ), so that the \\(c\\) -axis velocity field on the unit sphere is \\[ {\\bf \\dot{c}} = ({\\bf W} + {\\bf W}_{\\mathrm{p}}) \\cdot {\\hat {\\bf r}} \\quad\\text{($c$-axis velocity field on $S^2$)} , \\] where \\({\\hat {\\bf r}}\\) is the radial unit vector, and the plastic spin depends on \\({\\bf D}\\) to lowest and second lowest order following Wang (1969) and Aravas (1994) : \\[ {\\bf W}_{\\mathrm{p}} = \\iota({\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}\\cdot{\\bf D} - {\\bf D}\\cdot{\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}) + \\zeta({\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}\\cdot{\\bf D}^2 - {\\bf D}^2\\cdot{\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}) . \\] By requiring that basal planes preserve their orientation when subject to simple shear (like a deck of cards), it can be shown that \\(\\iota=1\\) and \\(\\zeta=0\\) . The corresponding effect on the continuous \\(c\\) -axis distribution is modelled as a conservative advection process on the surface of the unit sphere: \\[ \\frac{\\mathrm{D} n}{\\mathrm{D} t} = -\\nabla_{S^2}\\cdot(n{\\bf \\dot{c}}) \\quad\\Longrightarrow\\quad \\frac{\\mathrm{D} {\\bf s}}{\\mathrm{D} t} = {\\bf M_{\\mathrm{LROT}}} \\cdot {\\bf s}, \\] where \\({\\bf M_{\\mathrm{LROT}}}\\) is given analytically in Rathmann et al. (2021) . c-axis velocity field The normalized \\(c\\) -axis velocity fields for the three modes of deformation considered are: Example import numpy as np from specfabpy import specfab as sf # L=8 truncation is sufficient in this case, but larger L allows a very strong fabric to develop # and minimizes the effect that regularization has on low wavenumber modes (l=2,4) lm, nlm_len = sf.init(8) ### Velocity gradient tensor experienced by parcel ugrad = np.diag([0.5, 0.5, -1.0]) # Unconfined compression along z-direction (equal extension in x and y) D = (ugrad+np.transpose(ugrad))/2 # Symmetric part (strain-rate tensor) W = (ugrad-np.transpose(ugrad))/2 # Anti-symmetric part (spin tensor) ### Numerics Nt = 25 # Number of time steps dt = 0.05 # Time-step size ### Initialize fabric as isotropic nlm = np.zeros((Nt,nlm_len), dtype=np.complex64) # State vector (array of expansion coefficients) nlm[0,0] = 1/np.sqrt(4*np.pi) # Normalized ODF at t=0 ### Euler integration of lattice rotation + regularization for tt in np.arange(1,Nt): nlm_prev = nlm[tt-1,:] # Previous solution iota, zeta = 1, 0 # \"Deck of cards\" behavior M_LROT = sf.M_LROT(nlm_prev, D, W, iota, zeta) # Lattice rotation operator (nlm_len x nlm_len matrix) M_REG = sf.M_REG(nlm_prev, D) # Regularization operator (nlm_len x nlm_len matrix) M = M_LROT + M_REG nlm[tt,:] = nlm_prev + dt*np.matmul(M, nlm_prev) # Euler step nlm[tt,:] = sf.apply_bounds(nlm[tt,:]) # Apply spectral bounds if needed # See page \"Miscellaneous --> Plotting\" for how to plot resulting ODFs given nlm Discontinous dynamic recrystallization (DDRX) DDRX is modelled as a grain orientation or mass decay/production process on the unit sphere ( Placidi and others, 2010 ): \\[ \\frac{\\mathrm{D} n}{\\mathrm{D} t} = \\Gamma n \\quad\\Longrightarrow\\quad \\frac{\\mathrm{D} {\\bf s}}{\\mathrm{D} t} = {\\bf M_{\\mathrm{DDRX}}} \\cdot {\\bf s} , \\] where the decay/production rate \\[\\Gamma = \\Gamma_0\\left(D- {\\langle} D {\\rangle}\\right) \\quad\\text{(decay/production rate)}\\] depends on the rate magnitude \\(\\Gamma_0\\) , and the deformability \\(D\\) as a function of the stress tensor \\({\\bf S}\\) : \\[ D = \\frac{({\\bf S}\\cdot{\\bf S}):({\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}) - {\\bf S}:({\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}):{\\bf S}}{{\\bf S}:{\\bf S}}\\quad\\text{(deformability)} . \\] \\({\\bf M_{\\mathrm{DDRX}}}\\) is given analytically in Rathmann and Lilien (2021) . Note The average deformability, \\(\\langle D\\rangle\\) , depends on the instantaneous CPO state \u2014 specifically, the structure tensors a2 and a4 \u2014 making the corresponding matrix problem nonlinear by conserving the total number of grain orientations or mass density depending on how normalization is interpreted , the latter arguably resting on stronger physical grounds. Decay/production rate The normalized DDRX decay/production rate \\(\\Gamma/\\Gamma_0 = D - \\langle D \\rangle\\) is an orientation dependent field that favors nucleation (orientation/mass production) in the directions where the resolved basal-plane shear stress is maximal, and orientation/mass decay elsewhere. The the normalized decay rate for the three modes of deformation considered are: Example import numpy as np from specfabpy import specfab as sf # L=8 truncation is sufficient in this case, but larger L allows a very strong fabric to develop # and minimizes the effect that regularization has on low wavenumber modes (l=2,4) lm, nlm_len = sf.init(8) ### Stress tensor experienced by parcel S = np.diag([0.0, 1.0, -1.0]) # Confined compression along z-direction (extension confined to y-direction) #S = np.diag([0.5, 0.5, -1.0]) # Unconfined compression along z-direction Gamma0 = 10 # DDRX decay-rate magnitude (may depend on temperature, strain-rate, and other factors, see e.g. Richards et al., 2021) ### Numerics Nt = 25 # Number of time steps dt = 0.05 # Time-step size ### Initialize fabric as isotropic nlm = np.zeros((Nt,nlm_len), dtype=np.complex64) # State vector (array of expansion coefficients) nlm[0,0] = 1/np.sqrt(4*np.pi) # Normalized ODF at t=0 ### Euler integration of DDRX for tt in np.arange(1,Nt): nlm_prev = nlm[tt-1,:] # Previous solution M = Gamma0 * sf.M_DDRX(nlm_prev, S) # DDRX operator (nlm_len x nlm_len matrix) nlm[tt,:] = nlm_prev + dt*np.matmul(M, nlm_prev) # Complete Euler step nlm[tt,:] = sf.apply_bounds(nlm[tt,:]) # Apply spectral bounds if needed # See page \"Miscellaneous --> Plotting\" for how to plot resulting ODFs given nlm Continous dynamic recrystallization (CDRX) Polygonization (rotation recrystallization, CDRX) accounts for the division of grains along internal sub-grain boundaries when exposed to bending stresses. In effect, CDRX reduces the average grain size upon grain division but does not necessarily change the CPO much ( Alley, 1992 ). The model follows G\u00f6dert (2003) by approximating the effect of CDRX on the distribution of grain orientations/mass as a Laplacian diffusive process on \\(S^2\\) : \\[ \\frac{\\mathrm{D} n}{\\mathrm{D} t} = \\Lambda\\nabla^2 n \\quad\\Longrightarrow\\quad \\frac{\\mathrm{D} {\\bf s}}{\\mathrm{D} t} = {\\bf M_{\\mathrm{CDRX}}} \\cdot {\\bf s} . \\] Example To model CDRX, add the following contribution to the total fabric operator \\({\\bf M}\\) : M += Lambda*sf.M_CDRX(nlm) where Lambda is the CDRX rate-factor magnitude, \\(\\Lambda\\) , that possibly depends on temperature, stress, strain-rate, etc. ( Richards et al., 2021 ). Regularization As \\(n(\\theta,\\phi)\\) becomes anisotropic due to CPO processes, the coefficients \\(n_l^m\\) associated with high wavenumber modes (large \\(l\\) and \\(m\\) , and thus small-scale structure) must increase in magnitude relative to the low wavenumber coefficients (small \\(l\\) and \\(m\\) ). One way to visualize this is by the angular power spectrum \\[ S(l) = \\frac{1}{2l + 1} \\sum_{m=-l}^l \\left\\vert n_l^m \\right\\vert^2 , \\] which grows with time. In the animation above, the left-hand panel shows how the power spectrum evolves under lattice rotation (unconfined vertical compression) compared to the end-member case of a delta function (dashed line). If the expansion series is truncated at \\(l=L\\) , then \\(l{\\gt}L\\) modes cannot evolve, and the truncated solution will reach an unphysical quasi-steady state. To prevent this, regularization must be introduced. Specfab uses Laplacian hyper diffusion ( \\(k>1\\) ) as regularization in \\(S^2\\) \\[ \\frac{\\mathrm{D} n_l^m}{\\mathrm{D} t} ={\\nu}[l(l+1)]^{k} n_l^m \\quad\\Longrightarrow\\quad \\frac{\\mathrm{D} {\\bf s}}{\\mathrm{D} t} = {\\bf M_{\\mathrm{REG}}} \\cdot {\\bf s} , \\] that can be added to the fabric evolution operator \\({\\bf M}\\) as follows: M += sf.M_REG(nlm, D) This allows the growth of high wavenumber modes to be disproportionately damped (green line compared to red line in animation above). Note As a rule-of-thumb, regularization affects the highest and next-highest modes \\(l{\\geq}L-2\\) and can therefore not be expected to evolve freely. This, in turn, means that structure tensors a2 and a4 , and hence calculated enhancement factors , might be affected by regularization unless \\(L{\\geq}8\\) is chosen. High-level integrator Alternatively, you can use the high-level Lagrangian parcel integrator for constant stress/strain-rate: import numpy as np from specfabpy import specfab as sf from specfabpy import integrator as sfint lm, nlm_len = sf.init(8) ### Process rate factors etc. iota, zeta = 1, 0 # deck-of-cards behaviour for lattice rotation (=None => disabled) nu = 1 # scale the regularization magnitude by this amount (=None => no regularization) Gamma0 = Lambda = None # DDRX and CDRX rate factors (=None => disabled) ### Mode of deformation (mod) and parcel strain target # Note: axes 0,1,2 = x,y,z, planes 0,1,2 = yz,xz,xy # See page \"Miscellaneous --> Deformation Modes\" for T and r definitions mod, target = dict(type='ps', axis=2, T=+1, r=0), -0.95 # uniaxial compression along z until strain_zz = target mod, target = dict(type='ps', axis=2, T=-1, r=0), 6 # uniaxial extension along z until strain_zz = target mod, target = dict(type='ss', plane=1, T=+1), np.deg2rad(80) # simple shear until arctan(strain_xz) = target ### Integrate parcel CPO evolution nlm0 = np.zeros((nlm_len), dtype=np.complex64) nlm0[0] = 1/np.sqrt(4*np.pi) # normalized and isotropic initial state Nt = 200 # Number of integration steps nlm, F, time, ugrad = sfint.lagrangianparcel(sf, mod, target, Nt=Nt, nlm0=nlm0, iota=iota, zeta=zeta, Gamma0=Gamma0, Lambda=Lambda, nu=nu) # returns (state vector, deformation gradient tensor, time vector, velocity gradient) # See page \"Miscellaneous --> Plotting\" for how to plot resulting ODFs given nlm and parcel shape given F Validation If the CPO is rotated into an approximately rotationally-symmetric frame about the \\(z\\) -axis, then only \\(n_l^0\\) components are nonzero. This conveniently allows validating modelled CPO processes by comparing modelled to observed correlations between, e.g., the lowest-order normalized components \\(\\hat{n}_2^0 = n_2^0/n_0^0\\) and \\(\\hat{n}_4^0 = n_4^0/n_0^0\\) . The below plot from Lilien et al. (2023) shows the observed correlation structure (markers) compared to the above CPO model(s) for different modes of deformation, suggesting that modelled CPO processes capture observations reasonably well.","title":"Transversely isotropic grains"},{"location":"cpo-dynamics-tranisotropic/#cpo-dynamics-for-transversely-isotropic-grains","text":"This tutorial focuses on modelling the CPO evolution of polycrystalline glacier ice. That is, the distribution \\(n(\\theta,\\phi)\\) of (easy) slip-plane normals of grains ( \\({\\bf n}={\\bf c}\\) ). Polycrystalline ice Ensemble of slip elements","title":"CPO dynamics for transversely isotropic grains"},{"location":"cpo-dynamics-tranisotropic/#problem","text":"Given the expansion \\[ n({\\bf x},t,\\theta,\\phi)=\\sum_{l=0}^{L}\\sum_{m=-l}^{l}n_{l}^{m}({\\bf x},t) Y_{l}^{m}(\\theta,\\phi) \\quad\\text{(distribution of slip-plane normals)}, \\] CPO evolution can be written as a matrix problem involving the state vector \\[ {\\bf s} = [n_0^0,n_2^{-2},n_2^{-1},n_2^{0},n_2^{1},n_2^{2},n_4^{-4},\\cdots,n_4^{4},\\cdots,n_L^{-L},\\cdots,n_L^{L}]^{\\mathrm{T}} \\quad\\text{(state vector)}, \\] such that \\[ \\frac{\\mathrm{D}{\\bf s}}{\\mathrm{D} t} = {\\bf M} \\cdot {\\bf s} \\quad\\text{(state evolution)}, \\] where the operator (matrix) \\({\\bf M}\\) represents the effect of a given CPO process, which may depend on stress, strain-rate, temperature, etc. The total effect of multiple processes acting simultaneously is simply \\[ {\\bf M} = {\\bf M_{\\mathrm{LROT}}} + {\\bf M_{\\mathrm{DDRX}}} + {\\bf M_{\\mathrm{CDRX}}} + \\cdots \\quad\\text{(operator)}. \\] Note \\(n(\\theta,\\phi)\\) may also be understood as the mass density fraction of grains with a given slip-plane normal orientation. See CPO representation for details.","title":"Problem"},{"location":"cpo-dynamics-tranisotropic/#lagrangian-parcel","text":"The tutorial shows how to model the CPO evolution of a Lagrangian material parcel subject to three different modes of deformation :","title":"Lagrangian parcel"},{"location":"cpo-dynamics-tranisotropic/#lattice-rotation","text":"The strain-induced rotation of \\(c\\) -axes is modelled given the (local) velocity gradient tensor \\(\\nabla {\\bf u}\\) ( Svendsen and Hutter, 1996 ). The model is a kinematic model in the sense that \\(c\\) -axes rotate in response to the bulk rate of stretching, \\({\\bf D}\\) , and spin, \\({\\bf W}\\) , thereby allowing the detailed microscopic stress and strain rate fields to be neglected and hence interactions between neighboring grains to be disregarded. The modelled \\(c\\) -axes are taken to rotate with the bulk continuum spin ( \\({\\bf W}\\) ), plus some plastic spin correction ( \\({\\bf W}_{\\mathrm{p}}\\) ), so that the \\(c\\) -axis velocity field on the unit sphere is \\[ {\\bf \\dot{c}} = ({\\bf W} + {\\bf W}_{\\mathrm{p}}) \\cdot {\\hat {\\bf r}} \\quad\\text{($c$-axis velocity field on $S^2$)} , \\] where \\({\\hat {\\bf r}}\\) is the radial unit vector, and the plastic spin depends on \\({\\bf D}\\) to lowest and second lowest order following Wang (1969) and Aravas (1994) : \\[ {\\bf W}_{\\mathrm{p}} = \\iota({\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}\\cdot{\\bf D} - {\\bf D}\\cdot{\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}) + \\zeta({\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}\\cdot{\\bf D}^2 - {\\bf D}^2\\cdot{\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}) . \\] By requiring that basal planes preserve their orientation when subject to simple shear (like a deck of cards), it can be shown that \\(\\iota=1\\) and \\(\\zeta=0\\) . The corresponding effect on the continuous \\(c\\) -axis distribution is modelled as a conservative advection process on the surface of the unit sphere: \\[ \\frac{\\mathrm{D} n}{\\mathrm{D} t} = -\\nabla_{S^2}\\cdot(n{\\bf \\dot{c}}) \\quad\\Longrightarrow\\quad \\frac{\\mathrm{D} {\\bf s}}{\\mathrm{D} t} = {\\bf M_{\\mathrm{LROT}}} \\cdot {\\bf s}, \\] where \\({\\bf M_{\\mathrm{LROT}}}\\) is given analytically in Rathmann et al. (2021) . c-axis velocity field The normalized \\(c\\) -axis velocity fields for the three modes of deformation considered are:","title":"Lattice rotation"},{"location":"cpo-dynamics-tranisotropic/#example","text":"import numpy as np from specfabpy import specfab as sf # L=8 truncation is sufficient in this case, but larger L allows a very strong fabric to develop # and minimizes the effect that regularization has on low wavenumber modes (l=2,4) lm, nlm_len = sf.init(8) ### Velocity gradient tensor experienced by parcel ugrad = np.diag([0.5, 0.5, -1.0]) # Unconfined compression along z-direction (equal extension in x and y) D = (ugrad+np.transpose(ugrad))/2 # Symmetric part (strain-rate tensor) W = (ugrad-np.transpose(ugrad))/2 # Anti-symmetric part (spin tensor) ### Numerics Nt = 25 # Number of time steps dt = 0.05 # Time-step size ### Initialize fabric as isotropic nlm = np.zeros((Nt,nlm_len), dtype=np.complex64) # State vector (array of expansion coefficients) nlm[0,0] = 1/np.sqrt(4*np.pi) # Normalized ODF at t=0 ### Euler integration of lattice rotation + regularization for tt in np.arange(1,Nt): nlm_prev = nlm[tt-1,:] # Previous solution iota, zeta = 1, 0 # \"Deck of cards\" behavior M_LROT = sf.M_LROT(nlm_prev, D, W, iota, zeta) # Lattice rotation operator (nlm_len x nlm_len matrix) M_REG = sf.M_REG(nlm_prev, D) # Regularization operator (nlm_len x nlm_len matrix) M = M_LROT + M_REG nlm[tt,:] = nlm_prev + dt*np.matmul(M, nlm_prev) # Euler step nlm[tt,:] = sf.apply_bounds(nlm[tt,:]) # Apply spectral bounds if needed # See page \"Miscellaneous --> Plotting\" for how to plot resulting ODFs given nlm","title":"Example"},{"location":"cpo-dynamics-tranisotropic/#discontinous-dynamic-recrystallization-ddrx","text":"DDRX is modelled as a grain orientation or mass decay/production process on the unit sphere ( Placidi and others, 2010 ): \\[ \\frac{\\mathrm{D} n}{\\mathrm{D} t} = \\Gamma n \\quad\\Longrightarrow\\quad \\frac{\\mathrm{D} {\\bf s}}{\\mathrm{D} t} = {\\bf M_{\\mathrm{DDRX}}} \\cdot {\\bf s} , \\] where the decay/production rate \\[\\Gamma = \\Gamma_0\\left(D- {\\langle} D {\\rangle}\\right) \\quad\\text{(decay/production rate)}\\] depends on the rate magnitude \\(\\Gamma_0\\) , and the deformability \\(D\\) as a function of the stress tensor \\({\\bf S}\\) : \\[ D = \\frac{({\\bf S}\\cdot{\\bf S}):({\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}) - {\\bf S}:({\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}):{\\bf S}}{{\\bf S}:{\\bf S}}\\quad\\text{(deformability)} . \\] \\({\\bf M_{\\mathrm{DDRX}}}\\) is given analytically in Rathmann and Lilien (2021) . Note The average deformability, \\(\\langle D\\rangle\\) , depends on the instantaneous CPO state \u2014 specifically, the structure tensors a2 and a4 \u2014 making the corresponding matrix problem nonlinear by conserving the total number of grain orientations or mass density depending on how normalization is interpreted , the latter arguably resting on stronger physical grounds. Decay/production rate The normalized DDRX decay/production rate \\(\\Gamma/\\Gamma_0 = D - \\langle D \\rangle\\) is an orientation dependent field that favors nucleation (orientation/mass production) in the directions where the resolved basal-plane shear stress is maximal, and orientation/mass decay elsewhere. The the normalized decay rate for the three modes of deformation considered are:","title":"Discontinous dynamic recrystallization (DDRX)"},{"location":"cpo-dynamics-tranisotropic/#example_1","text":"import numpy as np from specfabpy import specfab as sf # L=8 truncation is sufficient in this case, but larger L allows a very strong fabric to develop # and minimizes the effect that regularization has on low wavenumber modes (l=2,4) lm, nlm_len = sf.init(8) ### Stress tensor experienced by parcel S = np.diag([0.0, 1.0, -1.0]) # Confined compression along z-direction (extension confined to y-direction) #S = np.diag([0.5, 0.5, -1.0]) # Unconfined compression along z-direction Gamma0 = 10 # DDRX decay-rate magnitude (may depend on temperature, strain-rate, and other factors, see e.g. Richards et al., 2021) ### Numerics Nt = 25 # Number of time steps dt = 0.05 # Time-step size ### Initialize fabric as isotropic nlm = np.zeros((Nt,nlm_len), dtype=np.complex64) # State vector (array of expansion coefficients) nlm[0,0] = 1/np.sqrt(4*np.pi) # Normalized ODF at t=0 ### Euler integration of DDRX for tt in np.arange(1,Nt): nlm_prev = nlm[tt-1,:] # Previous solution M = Gamma0 * sf.M_DDRX(nlm_prev, S) # DDRX operator (nlm_len x nlm_len matrix) nlm[tt,:] = nlm_prev + dt*np.matmul(M, nlm_prev) # Complete Euler step nlm[tt,:] = sf.apply_bounds(nlm[tt,:]) # Apply spectral bounds if needed # See page \"Miscellaneous --> Plotting\" for how to plot resulting ODFs given nlm","title":"Example"},{"location":"cpo-dynamics-tranisotropic/#continous-dynamic-recrystallization-cdrx","text":"Polygonization (rotation recrystallization, CDRX) accounts for the division of grains along internal sub-grain boundaries when exposed to bending stresses. In effect, CDRX reduces the average grain size upon grain division but does not necessarily change the CPO much ( Alley, 1992 ). The model follows G\u00f6dert (2003) by approximating the effect of CDRX on the distribution of grain orientations/mass as a Laplacian diffusive process on \\(S^2\\) : \\[ \\frac{\\mathrm{D} n}{\\mathrm{D} t} = \\Lambda\\nabla^2 n \\quad\\Longrightarrow\\quad \\frac{\\mathrm{D} {\\bf s}}{\\mathrm{D} t} = {\\bf M_{\\mathrm{CDRX}}} \\cdot {\\bf s} . \\]","title":"Continous dynamic recrystallization (CDRX)"},{"location":"cpo-dynamics-tranisotropic/#example_2","text":"To model CDRX, add the following contribution to the total fabric operator \\({\\bf M}\\) : M += Lambda*sf.M_CDRX(nlm) where Lambda is the CDRX rate-factor magnitude, \\(\\Lambda\\) , that possibly depends on temperature, stress, strain-rate, etc. ( Richards et al., 2021 ).","title":"Example"},{"location":"cpo-dynamics-tranisotropic/#regularization","text":"As \\(n(\\theta,\\phi)\\) becomes anisotropic due to CPO processes, the coefficients \\(n_l^m\\) associated with high wavenumber modes (large \\(l\\) and \\(m\\) , and thus small-scale structure) must increase in magnitude relative to the low wavenumber coefficients (small \\(l\\) and \\(m\\) ). One way to visualize this is by the angular power spectrum \\[ S(l) = \\frac{1}{2l + 1} \\sum_{m=-l}^l \\left\\vert n_l^m \\right\\vert^2 , \\] which grows with time. In the animation above, the left-hand panel shows how the power spectrum evolves under lattice rotation (unconfined vertical compression) compared to the end-member case of a delta function (dashed line). If the expansion series is truncated at \\(l=L\\) , then \\(l{\\gt}L\\) modes cannot evolve, and the truncated solution will reach an unphysical quasi-steady state. To prevent this, regularization must be introduced. Specfab uses Laplacian hyper diffusion ( \\(k>1\\) ) as regularization in \\(S^2\\) \\[ \\frac{\\mathrm{D} n_l^m}{\\mathrm{D} t} ={\\nu}[l(l+1)]^{k} n_l^m \\quad\\Longrightarrow\\quad \\frac{\\mathrm{D} {\\bf s}}{\\mathrm{D} t} = {\\bf M_{\\mathrm{REG}}} \\cdot {\\bf s} , \\] that can be added to the fabric evolution operator \\({\\bf M}\\) as follows: M += sf.M_REG(nlm, D) This allows the growth of high wavenumber modes to be disproportionately damped (green line compared to red line in animation above). Note As a rule-of-thumb, regularization affects the highest and next-highest modes \\(l{\\geq}L-2\\) and can therefore not be expected to evolve freely. This, in turn, means that structure tensors a2 and a4 , and hence calculated enhancement factors , might be affected by regularization unless \\(L{\\geq}8\\) is chosen.","title":"Regularization"},{"location":"cpo-dynamics-tranisotropic/#high-level-integrator","text":"Alternatively, you can use the high-level Lagrangian parcel integrator for constant stress/strain-rate: import numpy as np from specfabpy import specfab as sf from specfabpy import integrator as sfint lm, nlm_len = sf.init(8) ### Process rate factors etc. iota, zeta = 1, 0 # deck-of-cards behaviour for lattice rotation (=None => disabled) nu = 1 # scale the regularization magnitude by this amount (=None => no regularization) Gamma0 = Lambda = None # DDRX and CDRX rate factors (=None => disabled) ### Mode of deformation (mod) and parcel strain target # Note: axes 0,1,2 = x,y,z, planes 0,1,2 = yz,xz,xy # See page \"Miscellaneous --> Deformation Modes\" for T and r definitions mod, target = dict(type='ps', axis=2, T=+1, r=0), -0.95 # uniaxial compression along z until strain_zz = target mod, target = dict(type='ps', axis=2, T=-1, r=0), 6 # uniaxial extension along z until strain_zz = target mod, target = dict(type='ss', plane=1, T=+1), np.deg2rad(80) # simple shear until arctan(strain_xz) = target ### Integrate parcel CPO evolution nlm0 = np.zeros((nlm_len), dtype=np.complex64) nlm0[0] = 1/np.sqrt(4*np.pi) # normalized and isotropic initial state Nt = 200 # Number of integration steps nlm, F, time, ugrad = sfint.lagrangianparcel(sf, mod, target, Nt=Nt, nlm0=nlm0, iota=iota, zeta=zeta, Gamma0=Gamma0, Lambda=Lambda, nu=nu) # returns (state vector, deformation gradient tensor, time vector, velocity gradient) # See page \"Miscellaneous --> Plotting\" for how to plot resulting ODFs given nlm and parcel shape given F","title":"High-level integrator"},{"location":"cpo-dynamics-tranisotropic/#validation","text":"If the CPO is rotated into an approximately rotationally-symmetric frame about the \\(z\\) -axis, then only \\(n_l^0\\) components are nonzero. This conveniently allows validating modelled CPO processes by comparing modelled to observed correlations between, e.g., the lowest-order normalized components \\(\\hat{n}_2^0 = n_2^0/n_0^0\\) and \\(\\hat{n}_4^0 = n_4^0/n_0^0\\) . The below plot from Lilien et al. (2023) shows the observed correlation structure (markers) compared to the above CPO model(s) for different modes of deformation, suggesting that modelled CPO processes capture observations reasonably well.","title":"Validation"},{"location":"cpo-idealized/","text":"Idealized CPOs Three types of idealized CPO states can be said to exist: Unidirectional CPO : crystallographic axes are perfectly aligned, i.e. perfect single maximum. Planar CPO : crystallographic axes are perfectly distributed on a plane, i.e. a great circle on \\(S^2\\) . Circle CPO : crystallographic axes are perfectly distributed on a small circle on \\(S^2\\) . Each of these can be expanded in terms of spherical harmonics by using the sifting property of the delta function, \\(\\delta({\\hat {\\bf r}})\\) . Unidirectional Consider the case where grains are perfectly aligned with \\({{\\bf m}}\\) such that \\(n({\\hat {\\bf r}}) = \\delta({\\hat {\\bf r}}-{{\\bf m}})\\) . The corresponding expansion coefficients follow from the usual overlap integral: \\[ n_l^m = \\int_{S^2} \\delta(\\hat{{\\bf r}}-{{\\bf m}}) (Y_l^m(\\hat{{\\bf r}}))^* \\,\\mathrm{d}\\Omega = (Y_l^m({{\\bf m}}))^* . \\] In the figure below, the resulting unidirectional distribution is shown (rightmost inset), where the white area represents the subspace of possible CPOs when expressed in terms of the normalized coefficients of lowest order: \\(\\hat{n}_2^0 = n_2^0/n_0^0\\) and \\(\\hat{n}_4^0 = n_4^0/n_0^0\\) . The code below demonstrates how to generate the distribution with specfab. import numpy as np from specfabpy import specfab as sf L = 8 lm, nlm_len = sf.init(L) m = [0,0,1] # symmetry axis of distribution colat = 0 # 0 = unidirectional distribution, pi/2 = planar distribution, and anything in between is a small circle distribution nlm = sf.nlm_ideal(m, colat, L) # note: only l<=12 coefs are determined even if L>12 Planar and circle Planar and circle distributions follow from averaging the delta function over a desired co-latitude \\(\\theta\\) \u2014 i.e. the co-latitude where \\(n(\\hat{{\\bf r}})\\) should be sharply defined \u2014 in which case all zonal structure vanishes ( \\(m\\neq 0\\) components vanish) and we are left with \\[ n_l^m(\\theta) = \\begin{cases} Y_l^0(\\theta, \\phi=0) \\qquad\\text{if}\\quad m=0\\\\ 0 \\qquad\\qquad\\qquad\\quad \\text{if} \\quad m\\neq 0 \\end{cases} . \\] Here, \\({{\\bf m}}\\) is to be understood as the rotational symmetry axis of \\(n(\\hat{{\\bf r}})\\) , and the co-latitude is defined w.r.t. \\({{\\bf m}}\\) , not \\(\\hat{{\\bf z}}\\) . The above figure also shows the resulting \\(n(\\hat{{\\bf r}})\\) for different \\(\\theta\\) , calculated using the same code as above but for nonzero colat .","title":"Idealized states"},{"location":"cpo-idealized/#idealized-cpos","text":"Three types of idealized CPO states can be said to exist: Unidirectional CPO : crystallographic axes are perfectly aligned, i.e. perfect single maximum. Planar CPO : crystallographic axes are perfectly distributed on a plane, i.e. a great circle on \\(S^2\\) . Circle CPO : crystallographic axes are perfectly distributed on a small circle on \\(S^2\\) . Each of these can be expanded in terms of spherical harmonics by using the sifting property of the delta function, \\(\\delta({\\hat {\\bf r}})\\) .","title":"Idealized CPOs"},{"location":"cpo-idealized/#unidirectional","text":"Consider the case where grains are perfectly aligned with \\({{\\bf m}}\\) such that \\(n({\\hat {\\bf r}}) = \\delta({\\hat {\\bf r}}-{{\\bf m}})\\) . The corresponding expansion coefficients follow from the usual overlap integral: \\[ n_l^m = \\int_{S^2} \\delta(\\hat{{\\bf r}}-{{\\bf m}}) (Y_l^m(\\hat{{\\bf r}}))^* \\,\\mathrm{d}\\Omega = (Y_l^m({{\\bf m}}))^* . \\] In the figure below, the resulting unidirectional distribution is shown (rightmost inset), where the white area represents the subspace of possible CPOs when expressed in terms of the normalized coefficients of lowest order: \\(\\hat{n}_2^0 = n_2^0/n_0^0\\) and \\(\\hat{n}_4^0 = n_4^0/n_0^0\\) . The code below demonstrates how to generate the distribution with specfab. import numpy as np from specfabpy import specfab as sf L = 8 lm, nlm_len = sf.init(L) m = [0,0,1] # symmetry axis of distribution colat = 0 # 0 = unidirectional distribution, pi/2 = planar distribution, and anything in between is a small circle distribution nlm = sf.nlm_ideal(m, colat, L) # note: only l<=12 coefs are determined even if L>12","title":"Unidirectional"},{"location":"cpo-idealized/#planar-and-circle","text":"Planar and circle distributions follow from averaging the delta function over a desired co-latitude \\(\\theta\\) \u2014 i.e. the co-latitude where \\(n(\\hat{{\\bf r}})\\) should be sharply defined \u2014 in which case all zonal structure vanishes ( \\(m\\neq 0\\) components vanish) and we are left with \\[ n_l^m(\\theta) = \\begin{cases} Y_l^0(\\theta, \\phi=0) \\qquad\\text{if}\\quad m=0\\\\ 0 \\qquad\\qquad\\qquad\\quad \\text{if} \\quad m\\neq 0 \\end{cases} . \\] Here, \\({{\\bf m}}\\) is to be understood as the rotational symmetry axis of \\(n(\\hat{{\\bf r}})\\) , and the co-latitude is defined w.r.t. \\({{\\bf m}}\\) , not \\(\\hat{{\\bf z}}\\) . The above figure also shows the resulting \\(n(\\hat{{\\bf r}})\\) for different \\(\\theta\\) , calculated using the same code as above but for nonzero colat .","title":"Planar and circle"},{"location":"cpo-representation/","text":"CPO representation CPOs are represented by their distributions of crystallographic axes in orientation space ( \\(S^2\\) ), neglecting grain sizes/mass and other topological information. Supported grain symmetry groups are: Grain symmetry CPO components Definition Transversely isotropic \\(n(\\theta,\\phi)\\) Distribution of slip-plane normals Orthotropic \\(n(\\theta,\\phi),\\,b(\\theta,\\phi)\\) Distribution of slip-plane normals and slip directions Thus, depending on which crystallographic slip system is preferentially activated, \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) may refer to the distributions of different crystallographic axes. Glacier ice Since ice grains are approximately transversely isotropic, tracking \\(n(\\theta,\\phi)\\) (the \\(c\\) -axis distribution) is sufficient for representing the CPO. Polycrystalline ice Ensemble of slip elements Olivine For orthotropic grains such as olivine, both \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) distributions must be tracked to represent the CPO. Note that \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) represent the distributions of particular crystallographic axes ( \\({\\bf m}'_i\\) ) depending on fabric type (A\u2014E type). Polycrystalline olivine Ensemble of slip elements ODF The orientation distribution function (ODF) of a given slip-system axis (crystallographic axis) \\(f\\in \\lbrace n,b\\rbrace\\) is defined as the normalized distribution \\[ \\mathrm{ODF} = \\frac{f(\\theta,\\phi)}{N} \\quad\\text{where}\\quad N=\\int_{S^2} f(\\theta,\\phi) \\,\\mathrm{d}\\Omega . \\] Normalization \\(n(\\theta,\\phi)\\) may be understood either as the number density of grains with a given slip-plane normal orientation, or as the mass density fraction ( Faria, 2006 ; Richards et al., 2021 ) of grains with a given slip-plane normal orientation; \\(\\varrho^*(\\theta,\\phi)\\) in literature. The same goes for \\(b(\\theta,\\phi)\\) . From specfab's point-of-view, the difference is a matter of normalization: since the models of CPO evolution (lattice rotation, DDRX, CDRX) conserve the normalization, the two views are effectively the same, not least because CPO-derived quantities depend on the normalized distributions (which are identical). The mass-density-fraction interpretation rests, however, on stronger physical grounds as mass is conserved but grain numbers are not. Harmonic expansion The distributions \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) are represented as spherical harmonic expansion series: \\[ n(\\theta,\\phi)=\\sum_{l=0}^{L}\\sum_{m=-l}^{l}n_{l}^{m}Y_{l}^{m}(\\theta,\\phi) \\quad\\text{(distribution of slip-plane normals)}, \\] \\[ b(\\theta,\\phi)=\\sum_{l=0}^{L}\\sum_{m=-l}^{l}b_{l}^{m}Y_{l}^{m}(\\theta,\\phi) \\quad\\text{(distribution of slip directions)}. \\] The CPO state is thus described by the state vectors of complex-valued expansion coefficients \\[ {\\bf s}_n = [n_0^0,n_2^{-2},n_2^{-1},n_2^{0},n_2^{1},n_2^{2},n_4^{-4},\\cdots,n_4^{4},\\cdots,n_L^{-L},\\cdots,n_L^{L}] \\quad\\text{($n$ state vector)}, \\] \\[ {\\bf s}_b = [b_0^0,b_2^{-2},b_2^{-1},b_2^{0},b_2^{1},b_2^{2},b_4^{-4},\\cdots,b_4^{4},\\cdots,b_L^{-L},\\cdots,b_L^{L}] \\quad\\text{($b$ state vector)}, \\] where the magnitude and complex phase of the coefficients determine the size and rotation of the contribution from the associated harmonic mode. Reduced form Not all expansion coefficients are independent for real-valued expansion series, but must fulfill (likewise for \\(b\\) ) \\[ n_l^{-m}=(-1)^m(n_l^m)^* . \\] This can be taken advantage of for large problems where many (e.g. gridded) CPOs must be stored in memory, thereby effectively reducing the size of the problem. The vector of reduced expansion coefficients is defined as \\[ \\tilde{{\\bf s}}= [n_0^0,n_2^{0},n_2^{1},n_2^{2},n_4^{0},\\cdots,n_4^{4},\\cdots,n_L^{0},\\cdots,n_L^{L}] \\quad\\text{(reduced state vector)}. \\] Converting between full and reduced forms is done as follows: import numpy as np from specfabpy import specfabpy as sf lm, nlm_len = sf.init(2) # L=2 truncation is sufficient in this case ### Construct an arbitrary fabric a2 = np.diag([0.1,0.2,0.7]) # arbitrary second-order structure tensor nlm = np.zeros((nlm_len), dtype=np.complex64) # array of expansion coefficients nlm[:sf.L2len] = sf.a2_to_nlm(a2) # determine l<=2 expansion coefficients of ODF print('original:', nlm) ### Get reduced form of coefficient array, rnlm rnlm_len = sf.get_rnlm_len() rnlm = np.zeros((rnlm_len), dtype=np.complex64) # array of reduced expansion coefficients rnlm[:] = sf.nlm_to_rnlm(nlm, rnlm_len) # reduced form print('reduced:', rnlm) ### Recover full form (nlm) from reduced form (rnlm) nlm[:] = sf.rnlm_to_nlm(rnlm, nlm_len) print('recovered:', nlm)","title":"Representation"},{"location":"cpo-representation/#cpo-representation","text":"CPOs are represented by their distributions of crystallographic axes in orientation space ( \\(S^2\\) ), neglecting grain sizes/mass and other topological information. Supported grain symmetry groups are: Grain symmetry CPO components Definition Transversely isotropic \\(n(\\theta,\\phi)\\) Distribution of slip-plane normals Orthotropic \\(n(\\theta,\\phi),\\,b(\\theta,\\phi)\\) Distribution of slip-plane normals and slip directions Thus, depending on which crystallographic slip system is preferentially activated, \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) may refer to the distributions of different crystallographic axes. Glacier ice Since ice grains are approximately transversely isotropic, tracking \\(n(\\theta,\\phi)\\) (the \\(c\\) -axis distribution) is sufficient for representing the CPO. Polycrystalline ice Ensemble of slip elements Olivine For orthotropic grains such as olivine, both \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) distributions must be tracked to represent the CPO. Note that \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) represent the distributions of particular crystallographic axes ( \\({\\bf m}'_i\\) ) depending on fabric type (A\u2014E type). Polycrystalline olivine Ensemble of slip elements","title":"CPO representation"},{"location":"cpo-representation/#odf","text":"The orientation distribution function (ODF) of a given slip-system axis (crystallographic axis) \\(f\\in \\lbrace n,b\\rbrace\\) is defined as the normalized distribution \\[ \\mathrm{ODF} = \\frac{f(\\theta,\\phi)}{N} \\quad\\text{where}\\quad N=\\int_{S^2} f(\\theta,\\phi) \\,\\mathrm{d}\\Omega . \\]","title":"ODF"},{"location":"cpo-representation/#normalization","text":"\\(n(\\theta,\\phi)\\) may be understood either as the number density of grains with a given slip-plane normal orientation, or as the mass density fraction ( Faria, 2006 ; Richards et al., 2021 ) of grains with a given slip-plane normal orientation; \\(\\varrho^*(\\theta,\\phi)\\) in literature. The same goes for \\(b(\\theta,\\phi)\\) . From specfab's point-of-view, the difference is a matter of normalization: since the models of CPO evolution (lattice rotation, DDRX, CDRX) conserve the normalization, the two views are effectively the same, not least because CPO-derived quantities depend on the normalized distributions (which are identical). The mass-density-fraction interpretation rests, however, on stronger physical grounds as mass is conserved but grain numbers are not.","title":"Normalization"},{"location":"cpo-representation/#harmonic-expansion","text":"The distributions \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) are represented as spherical harmonic expansion series: \\[ n(\\theta,\\phi)=\\sum_{l=0}^{L}\\sum_{m=-l}^{l}n_{l}^{m}Y_{l}^{m}(\\theta,\\phi) \\quad\\text{(distribution of slip-plane normals)}, \\] \\[ b(\\theta,\\phi)=\\sum_{l=0}^{L}\\sum_{m=-l}^{l}b_{l}^{m}Y_{l}^{m}(\\theta,\\phi) \\quad\\text{(distribution of slip directions)}. \\] The CPO state is thus described by the state vectors of complex-valued expansion coefficients \\[ {\\bf s}_n = [n_0^0,n_2^{-2},n_2^{-1},n_2^{0},n_2^{1},n_2^{2},n_4^{-4},\\cdots,n_4^{4},\\cdots,n_L^{-L},\\cdots,n_L^{L}] \\quad\\text{($n$ state vector)}, \\] \\[ {\\bf s}_b = [b_0^0,b_2^{-2},b_2^{-1},b_2^{0},b_2^{1},b_2^{2},b_4^{-4},\\cdots,b_4^{4},\\cdots,b_L^{-L},\\cdots,b_L^{L}] \\quad\\text{($b$ state vector)}, \\] where the magnitude and complex phase of the coefficients determine the size and rotation of the contribution from the associated harmonic mode.","title":"Harmonic expansion"},{"location":"cpo-representation/#reduced-form","text":"Not all expansion coefficients are independent for real-valued expansion series, but must fulfill (likewise for \\(b\\) ) \\[ n_l^{-m}=(-1)^m(n_l^m)^* . \\] This can be taken advantage of for large problems where many (e.g. gridded) CPOs must be stored in memory, thereby effectively reducing the size of the problem. The vector of reduced expansion coefficients is defined as \\[ \\tilde{{\\bf s}}= [n_0^0,n_2^{0},n_2^{1},n_2^{2},n_4^{0},\\cdots,n_4^{4},\\cdots,n_L^{0},\\cdots,n_L^{L}] \\quad\\text{(reduced state vector)}. \\] Converting between full and reduced forms is done as follows: import numpy as np from specfabpy import specfabpy as sf lm, nlm_len = sf.init(2) # L=2 truncation is sufficient in this case ### Construct an arbitrary fabric a2 = np.diag([0.1,0.2,0.7]) # arbitrary second-order structure tensor nlm = np.zeros((nlm_len), dtype=np.complex64) # array of expansion coefficients nlm[:sf.L2len] = sf.a2_to_nlm(a2) # determine l<=2 expansion coefficients of ODF print('original:', nlm) ### Get reduced form of coefficient array, rnlm rnlm_len = sf.get_rnlm_len() rnlm = np.zeros((rnlm_len), dtype=np.complex64) # array of reduced expansion coefficients rnlm[:] = sf.nlm_to_rnlm(nlm, rnlm_len) # reduced form print('reduced:', rnlm) ### Recover full form (nlm) from reduced form (rnlm) nlm[:] = sf.rnlm_to_nlm(rnlm, nlm_len) print('recovered:', nlm)","title":"Reduced form"},{"location":"cpo-rotation/","text":"Rotation Rotating an expansion series by \\(\\theta\\) about the \\(y\\) -axis (in the \\(x\\) \u2014 \\(z\\) plane) followed by \\(\\phi\\) about the \\(z\\) -axis (in the \\(x\\) \u2014 \\(y\\) plane) can be done as follow: import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(8) ### Construct an arbitrary fabric to rotate a2 = np.diag([0, 0, 1]) # arbitrary second-order structure tensor, a^(2) nlm = np.zeros((nlm_len), dtype=np.complex64) # array of expansion coefficients nlm[:sf.L2len] = sf.a2_to_nlm(a2) # l<=2 expansion coefficients of corresponding ODF ### Rotate ODF # Note: assumes L=<12 (rotation for larger L is not implemented) theta = np.deg2rad(-45) phi = np.deg2rad(45) nlm_rot1 = sf.rotate_nlm(nlm, theta, 0) # first rotate around y axis in x-z plane nlm_rot2 = sf.rotate_nlm(nlm_rot1, 0, phi) # next rotate around z axis in x-y plane nlm_rot3 = sf.rotate_nlm(nlm_rot2, -theta, -phi) # rotate back # See \"plotting\" pages on how to plot the resulting ODFs","title":"Rotation"},{"location":"cpo-rotation/#rotation","text":"Rotating an expansion series by \\(\\theta\\) about the \\(y\\) -axis (in the \\(x\\) \u2014 \\(z\\) plane) followed by \\(\\phi\\) about the \\(z\\) -axis (in the \\(x\\) \u2014 \\(y\\) plane) can be done as follow: import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(8) ### Construct an arbitrary fabric to rotate a2 = np.diag([0, 0, 1]) # arbitrary second-order structure tensor, a^(2) nlm = np.zeros((nlm_len), dtype=np.complex64) # array of expansion coefficients nlm[:sf.L2len] = sf.a2_to_nlm(a2) # l<=2 expansion coefficients of corresponding ODF ### Rotate ODF # Note: assumes L=<12 (rotation for larger L is not implemented) theta = np.deg2rad(-45) phi = np.deg2rad(45) nlm_rot1 = sf.rotate_nlm(nlm, theta, 0) # first rotate around y axis in x-z plane nlm_rot2 = sf.rotate_nlm(nlm_rot1, 0, phi) # next rotate around z axis in x-y plane nlm_rot3 = sf.rotate_nlm(nlm_rot2, -theta, -phi) # rotate back # See \"plotting\" pages on how to plot the resulting ODFs","title":"Rotation"},{"location":"cpo-structuretensors/","text":"Structure tensors The \\(k\\) -th order structure tensor (vector moment) is defined as the average \\(k\\) -th repeated outer product of a slip-system axis (crystallographic axis) with itself. For example, in the case of a discrete ensemble of slip plane normals (e.g. \\({\\bf n} = {\\bf c}\\) for ice) they are \\[ {\\bf a}^{(k)}({\\bf n}_i) = \\frac{1}{N}\\sum_i^N ({\\bf n}_i\\otimes)^k, \\] where \\(N\\) is the total number of grains, assuming equal grain weight (i.e. mass) for simplicity. Alternatively, if the distribution function of \\({\\bf n}\\) axes is known, i.e. \\(n(\\theta,\\phi)\\) , the structure tensors are \\[ {\\bf a}^{(k)}(n) = \\frac{1}{N} \\int_{S^2} (\\hat{{\\bf r}}\\otimes)^k n(\\theta,\\phi) \\, \\mathrm{d}\\Omega , \\] where \\(\\mathrm{d}\\Omega = \\sin(\\theta) \\mathrm{d}\\theta \\mathrm{d}\\phi\\) is the infinitesimal solid angle, \\(\\hat{{\\bf r}}(\\theta,\\phi)\\) is the radial unit vector, and \\(N=\\int_{S^2} n(\\theta,\\phi) \\, \\mathrm{d}\\Omega\\) . Principal frame Since \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) are antipodally symmetric, odd moments (odd \\(k\\) ) vanish identically. Hence, \\({\\bf a}^{(2)}(n)\\) and \\({\\bf a}^{(2)}(b)\\) measure the variance of \\(\\bf n\\) and \\(\\bf b\\) axes, respectively, around the three coordinate axes. Posing \\({\\bf a}^{(2)}\\) in its principal frame \\[ {\\bf a}^{(2)} = \\left[\\begin{matrix} \\lambda_1 & 0 & 0\\\\ 0 & \\lambda_2 & 0\\\\ 0 & 0 & \\lambda_3\\\\ \\end{matrix}\\right] \\] therefore has a similar interpretation as in PCA: the first principal component (eigenvector \\({\\bf m}_1\\) ) is the direction that maximizes the variance (eigenvalue \\(\\lambda_1\\) ) of the projected data (red curve), the second component is the direction orthogonal to the first component that maximizes the variance of the projected data, and so on with the third component. Convert to spectral Converting between spectral and tensorial representations is a linear problem in the sense that \\[ {\\bf a}^{(k)}(n) = {\\bf f}(\\hat{n}_2^{m}, \\hat{n}_4^{m}, \\cdots, \\hat{n}_k^{m}) , \\qquad\\text{(for all $m$)} \\] where \\({\\bf f}\\) is linear in its arguments, and \\[ \\hat{n}_l^m = n_l^m/n_0^0 . \\] In the case of \\({\\bf a}^{(2)}\\) the relation is simple: \\[ {\\bf a}^{(2)} = \\frac{{\\bf I}}{3} + \\sqrt{\\frac{2}{15}} \\left[\\begin{matrix} \\operatorname{Re}[\\hat{n}_2^2] - \\dfrac{1}{2}\\sqrt{\\dfrac{2}{3}} \\hat{n}_2^0 & -\\operatorname{Im}[\\hat{n}_2^2] & -\\operatorname{Re}[\\hat{n}_2^1] \\\\ & -\\operatorname{Re}[\\hat{n}_2^2] - \\dfrac{1}{2}\\sqrt{\\dfrac{2}{3}} \\hat{n}_2^0 & \\operatorname{Im}[\\hat{n}_2^1] \\\\ \\mathrm{sym.} & & \\sqrt{\\dfrac{2}{3}} \\hat{n}_2^0 \\end{matrix}\\right] , \\] but for higher-order structure tensors the expressions are long (not shown). The above applies to \\(b(\\theta,\\phi)\\) as well. The following code example shows how to convert between the representations: import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(8) nlm = np.zeros((nlm_len), dtype=np.complex64) # array of expansion coefficients ### a2 to nlm a2 = np.diag([0.0,0.25,0.75]) # arbitrary second-order structure tensor nlm[:sf.L2len] = sf.a2_to_nlm(a2) # determine l<=2 expansion coefficients of ODF a2 = sf.a2(nlm) # nlm back to a2 print('a2 is: ', a2) ### a4 to nlm p = np.array([0,0,1]) # unidirectional CPO a4 = np.einsum('i,j,k,l', p,p,p,p) # a4 for ODF = deltafunc(r-p) nlm[:sf.L4len] = sf.a4_to_nlm(a4) # determine l<=4 expansion coefficients of ODF a4 = sf.a4(nlm) # nlm back to a4 print('a4 is: ', a4) ### a6 to nlm p = np.array([0,0,1]) # unidirectional CPO a6 = np.einsum('i,j,k,l,m,n', p,p,p,p,p,p) # a6 for ODF = deltafunc(r-p) nlm[:sf.L6len] = sf.a6_to_nlm(a6) # determine l<=6 expansion coefficients of ODF a6 = sf.a6(nlm) # nlm back to a6 print('a6 is: ', a6) Construct from measurements The spectral expansion coefficients of any CPO may be determined from discrete measurements of crystallographic axes. This requires constructing the corresponding structure tensors (for each crystallographic axis), from which the expansion coefficients may be derived. import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(8) ### Replace with your own array/list of measured c-axes # caxes = [[c1x,c1y,c1z], [c2x,c2y,c2z], ...] ### Determine sixth-order structure tensor, a6 a6 = np.zeros((3,3,3,3,3,3)) for c in caxes a6 += np.einsum('i,j,k,l,m,n', c,c,c,c,c,c) # sixth outer product of c-axis with itself a6 /= len(caxes) # normalize by number of c-axes (grains) ### Determine spectral expansion coefficients nlm = np.zeros((nlm_len), dtype=np.complex64) # array of expansion coefficients nlm[:sf.L6len] = sf.a6_to_nlm(a6) # determine l<=6 expansion coefficients of ODF Note that constructing a6 is to be preferred over a4 and a2 since it contains more information on the fine-scale structure of the distribution; that is, \\(l\\leq 6\\) expansion coefficients as opposed to \\(l\\leq 4\\) and \\(l\\leq 2\\) coefficients, respectively.","title":"Structure tensors"},{"location":"cpo-structuretensors/#structure-tensors","text":"The \\(k\\) -th order structure tensor (vector moment) is defined as the average \\(k\\) -th repeated outer product of a slip-system axis (crystallographic axis) with itself. For example, in the case of a discrete ensemble of slip plane normals (e.g. \\({\\bf n} = {\\bf c}\\) for ice) they are \\[ {\\bf a}^{(k)}({\\bf n}_i) = \\frac{1}{N}\\sum_i^N ({\\bf n}_i\\otimes)^k, \\] where \\(N\\) is the total number of grains, assuming equal grain weight (i.e. mass) for simplicity. Alternatively, if the distribution function of \\({\\bf n}\\) axes is known, i.e. \\(n(\\theta,\\phi)\\) , the structure tensors are \\[ {\\bf a}^{(k)}(n) = \\frac{1}{N} \\int_{S^2} (\\hat{{\\bf r}}\\otimes)^k n(\\theta,\\phi) \\, \\mathrm{d}\\Omega , \\] where \\(\\mathrm{d}\\Omega = \\sin(\\theta) \\mathrm{d}\\theta \\mathrm{d}\\phi\\) is the infinitesimal solid angle, \\(\\hat{{\\bf r}}(\\theta,\\phi)\\) is the radial unit vector, and \\(N=\\int_{S^2} n(\\theta,\\phi) \\, \\mathrm{d}\\Omega\\) .","title":"Structure tensors"},{"location":"cpo-structuretensors/#principal-frame","text":"Since \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) are antipodally symmetric, odd moments (odd \\(k\\) ) vanish identically. Hence, \\({\\bf a}^{(2)}(n)\\) and \\({\\bf a}^{(2)}(b)\\) measure the variance of \\(\\bf n\\) and \\(\\bf b\\) axes, respectively, around the three coordinate axes. Posing \\({\\bf a}^{(2)}\\) in its principal frame \\[ {\\bf a}^{(2)} = \\left[\\begin{matrix} \\lambda_1 & 0 & 0\\\\ 0 & \\lambda_2 & 0\\\\ 0 & 0 & \\lambda_3\\\\ \\end{matrix}\\right] \\] therefore has a similar interpretation as in PCA: the first principal component (eigenvector \\({\\bf m}_1\\) ) is the direction that maximizes the variance (eigenvalue \\(\\lambda_1\\) ) of the projected data (red curve), the second component is the direction orthogonal to the first component that maximizes the variance of the projected data, and so on with the third component.","title":"Principal frame"},{"location":"cpo-structuretensors/#convert-to-spectral","text":"Converting between spectral and tensorial representations is a linear problem in the sense that \\[ {\\bf a}^{(k)}(n) = {\\bf f}(\\hat{n}_2^{m}, \\hat{n}_4^{m}, \\cdots, \\hat{n}_k^{m}) , \\qquad\\text{(for all $m$)} \\] where \\({\\bf f}\\) is linear in its arguments, and \\[ \\hat{n}_l^m = n_l^m/n_0^0 . \\] In the case of \\({\\bf a}^{(2)}\\) the relation is simple: \\[ {\\bf a}^{(2)} = \\frac{{\\bf I}}{3} + \\sqrt{\\frac{2}{15}} \\left[\\begin{matrix} \\operatorname{Re}[\\hat{n}_2^2] - \\dfrac{1}{2}\\sqrt{\\dfrac{2}{3}} \\hat{n}_2^0 & -\\operatorname{Im}[\\hat{n}_2^2] & -\\operatorname{Re}[\\hat{n}_2^1] \\\\ & -\\operatorname{Re}[\\hat{n}_2^2] - \\dfrac{1}{2}\\sqrt{\\dfrac{2}{3}} \\hat{n}_2^0 & \\operatorname{Im}[\\hat{n}_2^1] \\\\ \\mathrm{sym.} & & \\sqrt{\\dfrac{2}{3}} \\hat{n}_2^0 \\end{matrix}\\right] , \\] but for higher-order structure tensors the expressions are long (not shown). The above applies to \\(b(\\theta,\\phi)\\) as well. The following code example shows how to convert between the representations: import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(8) nlm = np.zeros((nlm_len), dtype=np.complex64) # array of expansion coefficients ### a2 to nlm a2 = np.diag([0.0,0.25,0.75]) # arbitrary second-order structure tensor nlm[:sf.L2len] = sf.a2_to_nlm(a2) # determine l<=2 expansion coefficients of ODF a2 = sf.a2(nlm) # nlm back to a2 print('a2 is: ', a2) ### a4 to nlm p = np.array([0,0,1]) # unidirectional CPO a4 = np.einsum('i,j,k,l', p,p,p,p) # a4 for ODF = deltafunc(r-p) nlm[:sf.L4len] = sf.a4_to_nlm(a4) # determine l<=4 expansion coefficients of ODF a4 = sf.a4(nlm) # nlm back to a4 print('a4 is: ', a4) ### a6 to nlm p = np.array([0,0,1]) # unidirectional CPO a6 = np.einsum('i,j,k,l,m,n', p,p,p,p,p,p) # a6 for ODF = deltafunc(r-p) nlm[:sf.L6len] = sf.a6_to_nlm(a6) # determine l<=6 expansion coefficients of ODF a6 = sf.a6(nlm) # nlm back to a6 print('a6 is: ', a6)","title":"Convert to spectral"},{"location":"cpo-structuretensors/#construct-from-measurements","text":"The spectral expansion coefficients of any CPO may be determined from discrete measurements of crystallographic axes. This requires constructing the corresponding structure tensors (for each crystallographic axis), from which the expansion coefficients may be derived. import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(8) ### Replace with your own array/list of measured c-axes # caxes = [[c1x,c1y,c1z], [c2x,c2y,c2z], ...] ### Determine sixth-order structure tensor, a6 a6 = np.zeros((3,3,3,3,3,3)) for c in caxes a6 += np.einsum('i,j,k,l,m,n', c,c,c,c,c,c) # sixth outer product of c-axis with itself a6 /= len(caxes) # normalize by number of c-axes (grains) ### Determine spectral expansion coefficients nlm = np.zeros((nlm_len), dtype=np.complex64) # array of expansion coefficients nlm[:sf.L6len] = sf.a6_to_nlm(a6) # determine l<=6 expansion coefficients of ODF Note that constructing a6 is to be preferred over a4 and a2 since it contains more information on the fine-scale structure of the distribution; that is, \\(l\\leq 6\\) expansion coefficients as opposed to \\(l\\leq 4\\) and \\(l\\leq 2\\) coefficients, respectively.","title":"Construct from measurements"},{"location":"deformation-modes/","text":"Deformation modes For a continuum subject to deformation, the deformation gradient tensor , \\({\\bf F}\\) , describes the relative change in position of material points. If \\({\\bf F}\\) is known, then the velocity gradient tensor follows as $$ \\nabla {\\bf u} = \\dot{{\\bf F}} {\\bf F}^{-1} . $$ The resulting strain experienced (strain tensor) is \\begin{align} {\\boldsymbol \\epsilon} = \\frac{1}{2}\\left( {\\bf F}+{\\bf F}^\\top \\right) - {\\bf I} . \\end{align} Below, we consider how to represent pure shear and simple shear with \\({\\bf F}\\) . Pure shear \\({\\bf F}\\) is diagonal for pure shear deformation when the principal strain axes are aligned with the coordinate system. Suppose shortening takes places along the vertical axis and lengthening in the horizontal plane, then \\[ {\\bf F}_{\\mathrm{P}} = \\begin{bmatrix} b^{(1+r)/2} & 0 & 0\\\\ 0 & b^{(1-r)/2} & 0\\\\ 0& 0& b^{-1} \\end{bmatrix} , \\] where the parameter \\(r\\in[-1;1]\\) controls the relative lengthening between two horizontal directions: for \\(r=0\\) lengthening is equal in the \\(x\\) and \\(y\\) directions, for \\(r=+1\\) lengthening occurs only in the \\(x\\) direction, for \\(r=-1\\) lengthening occurs only in the \\(y\\) direction. Calculating the velocity gradient tensor yields \\[ \\nabla {\\bf u} = \\frac{\\dot{b}}{b} \\begin{bmatrix} (1+r)/2 & 0 & 0\\\\ 0 & (1-r)/2 & 0\\\\ 0& 0& -1 \\end{bmatrix} . \\] If the scaling parameter, \\(b\\) , is written in terms of the \\(e\\) -folding time scale \\(T\\) as \\[ b(t) = \\exp(t/T) , \\] the strain-rate becomes constant, \\[ \\frac{\\dot{b}}{b} = \\frac{1}{T}. \\] Notice that the vertical strain experienced as a function of time is \\[ \\epsilon_{zz}(t) = \\frac{1}{b(t)} - 1, \\] where \\(\\epsilon_{zz} = 0\\) corresponds to an undeformed ice parcel ( \\(t=0\\) ), and \\(\\epsilon_{zz} = -1\\) is the limit of vanishing parcel height ( \\(t\\rightarrow\\infty\\) ). Consider instead the case where lengthening takes place along the vertical axis and shortening in the horizontal plane. This is achieved by substituting \\(b\\rightarrow b^{-1}\\) which implies \\({\\bf F}={\\bf F}_{\\mathrm{P}}^{-1}\\) and hence is the time reversed behavior of \\({\\bf F}_{\\mathrm{P}}\\) since \\(b^{-1}(t)=b(-t)\\) . Example The above expressions are accessible in specfab as follows import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(4) axis = 2 # axis of shortening (T>0) or lengthening (T<0): 0=x, 1=y, 2=z Tc = 1 # time taken in seconds for parcel to reduce to half (50%) height if T>0, or abs(time) taken for parcel to double in height (200%) if T<0. r = 0 # asymmetry parameter for shortening (if T>0) or lengthening (if T<0) T = Tc/np.log(2) # corresponding e-folding time ugrad = sf.pureshear_ugrad(axis, r, T) # velocity gradient D, W = sf.ugrad_to_D_and_W(ugrad) # strain-rate and spin tensor t = 1 # some specific time of interest b = sf.pureshear_b(T, t) # scaling parameter at time t F = sf.pureshear_F(axis, r, T, t) # deformation gradient tensor at time t eps = sf.F_to_strain(F) # strain tensor at time t Simple shear Simple shear strain may be characterized by the shear angle \\(\\gamma\\) of the resulting rhombus. In the case of vertical shear in the \\(x\\) \u2014 \\(z\\) plane, the deformation gradient tensor is given by \\[ {\\bf F}_{\\mathrm{S}} = \\begin{bmatrix} 1 & 0 & \\tan(\\gamma) \\\\ 0 & 1 & 0\\\\ 0& 0& 1 \\end{bmatrix} , \\] and velocity-gradient tensor becomes \\[ \\nabla {\\bf u} = \\frac{\\dot{\\gamma}}{\\cos^2(\\gamma)} \\begin{bmatrix} 0 & 0 & 1\\\\ 0 & 0 & 0\\\\ 0& 0& 0 \\end{bmatrix} . \\] For a constant shear rate, \\(1/T\\) , the shear-angle time dependence is \\begin{align} \\gamma(t) = \\tan^{-1}(t/T) , \\end{align} where \\(T\\) is the characteristic time taken to reach a shear of 1 from an undeformed state. In this case, \\(\\nabla {\\bf u}\\) is constant, too, since \\[ \\frac{\\dot{\\gamma}}{\\cos^2(\\gamma)} = \\frac{1}{T} . \\] Example The above expressions are accessible in specfab as follows import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(4) plane = 1 # plane of shear: 0=yz, 1=xz, 2=xy T = 1 # time taken in seconds for parcel to a reach shear strain of 1 (45 deg shear angle) ugrad = sf.simpleshear_ugrad(plane, T) # velocity gradient D, W = sf.ugrad_to_D_and_W(ugrad) # strain-rate and spin tensor t = 1 # some specific time of interest gamma = sf.simpleshear_gamma(T, t) # shear angle at time t F = sf.simpleshear_F(plane, T, t) # deformation gradient tensor at time t eps = sf.F_to_strain(F) # strain tensor at time t","title":"Deformation modes"},{"location":"deformation-modes/#deformation-modes","text":"For a continuum subject to deformation, the deformation gradient tensor , \\({\\bf F}\\) , describes the relative change in position of material points. If \\({\\bf F}\\) is known, then the velocity gradient tensor follows as $$ \\nabla {\\bf u} = \\dot{{\\bf F}} {\\bf F}^{-1} . $$ The resulting strain experienced (strain tensor) is \\begin{align} {\\boldsymbol \\epsilon} = \\frac{1}{2}\\left( {\\bf F}+{\\bf F}^\\top \\right) - {\\bf I} . \\end{align} Below, we consider how to represent pure shear and simple shear with \\({\\bf F}\\) .","title":"Deformation modes"},{"location":"deformation-modes/#pure-shear","text":"\\({\\bf F}\\) is diagonal for pure shear deformation when the principal strain axes are aligned with the coordinate system. Suppose shortening takes places along the vertical axis and lengthening in the horizontal plane, then \\[ {\\bf F}_{\\mathrm{P}} = \\begin{bmatrix} b^{(1+r)/2} & 0 & 0\\\\ 0 & b^{(1-r)/2} & 0\\\\ 0& 0& b^{-1} \\end{bmatrix} , \\] where the parameter \\(r\\in[-1;1]\\) controls the relative lengthening between two horizontal directions: for \\(r=0\\) lengthening is equal in the \\(x\\) and \\(y\\) directions, for \\(r=+1\\) lengthening occurs only in the \\(x\\) direction, for \\(r=-1\\) lengthening occurs only in the \\(y\\) direction. Calculating the velocity gradient tensor yields \\[ \\nabla {\\bf u} = \\frac{\\dot{b}}{b} \\begin{bmatrix} (1+r)/2 & 0 & 0\\\\ 0 & (1-r)/2 & 0\\\\ 0& 0& -1 \\end{bmatrix} . \\] If the scaling parameter, \\(b\\) , is written in terms of the \\(e\\) -folding time scale \\(T\\) as \\[ b(t) = \\exp(t/T) , \\] the strain-rate becomes constant, \\[ \\frac{\\dot{b}}{b} = \\frac{1}{T}. \\] Notice that the vertical strain experienced as a function of time is \\[ \\epsilon_{zz}(t) = \\frac{1}{b(t)} - 1, \\] where \\(\\epsilon_{zz} = 0\\) corresponds to an undeformed ice parcel ( \\(t=0\\) ), and \\(\\epsilon_{zz} = -1\\) is the limit of vanishing parcel height ( \\(t\\rightarrow\\infty\\) ). Consider instead the case where lengthening takes place along the vertical axis and shortening in the horizontal plane. This is achieved by substituting \\(b\\rightarrow b^{-1}\\) which implies \\({\\bf F}={\\bf F}_{\\mathrm{P}}^{-1}\\) and hence is the time reversed behavior of \\({\\bf F}_{\\mathrm{P}}\\) since \\(b^{-1}(t)=b(-t)\\) .","title":"Pure shear"},{"location":"deformation-modes/#example","text":"The above expressions are accessible in specfab as follows import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(4) axis = 2 # axis of shortening (T>0) or lengthening (T<0): 0=x, 1=y, 2=z Tc = 1 # time taken in seconds for parcel to reduce to half (50%) height if T>0, or abs(time) taken for parcel to double in height (200%) if T<0. r = 0 # asymmetry parameter for shortening (if T>0) or lengthening (if T<0) T = Tc/np.log(2) # corresponding e-folding time ugrad = sf.pureshear_ugrad(axis, r, T) # velocity gradient D, W = sf.ugrad_to_D_and_W(ugrad) # strain-rate and spin tensor t = 1 # some specific time of interest b = sf.pureshear_b(T, t) # scaling parameter at time t F = sf.pureshear_F(axis, r, T, t) # deformation gradient tensor at time t eps = sf.F_to_strain(F) # strain tensor at time t","title":"Example"},{"location":"deformation-modes/#simple-shear","text":"Simple shear strain may be characterized by the shear angle \\(\\gamma\\) of the resulting rhombus. In the case of vertical shear in the \\(x\\) \u2014 \\(z\\) plane, the deformation gradient tensor is given by \\[ {\\bf F}_{\\mathrm{S}} = \\begin{bmatrix} 1 & 0 & \\tan(\\gamma) \\\\ 0 & 1 & 0\\\\ 0& 0& 1 \\end{bmatrix} , \\] and velocity-gradient tensor becomes \\[ \\nabla {\\bf u} = \\frac{\\dot{\\gamma}}{\\cos^2(\\gamma)} \\begin{bmatrix} 0 & 0 & 1\\\\ 0 & 0 & 0\\\\ 0& 0& 0 \\end{bmatrix} . \\] For a constant shear rate, \\(1/T\\) , the shear-angle time dependence is \\begin{align} \\gamma(t) = \\tan^{-1}(t/T) , \\end{align} where \\(T\\) is the characteristic time taken to reach a shear of 1 from an undeformed state. In this case, \\(\\nabla {\\bf u}\\) is constant, too, since \\[ \\frac{\\dot{\\gamma}}{\\cos^2(\\gamma)} = \\frac{1}{T} . \\]","title":"Simple shear"},{"location":"deformation-modes/#example_1","text":"The above expressions are accessible in specfab as follows import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(4) plane = 1 # plane of shear: 0=yz, 1=xz, 2=xy T = 1 # time taken in seconds for parcel to a reach shear strain of 1 (45 deg shear angle) ugrad = sf.simpleshear_ugrad(plane, T) # velocity gradient D, W = sf.ugrad_to_D_and_W(ugrad) # strain-rate and spin tensor t = 1 # some specific time of interest gamma = sf.simpleshear_gamma(T, t) # shear angle at time t F = sf.simpleshear_F(plane, T, t) # deformation gradient tensor at time t eps = sf.F_to_strain(F) # strain tensor at time t","title":"Example"},{"location":"enhancements-strainrate/","text":"Strain-rate enhancements Given an anisotropic rheology \\({\\bf D}({\\bf S})\\) , where \\({\\bf D}\\) and \\({\\bf S}\\) are the strain-rate and deviatoric stress tensors, respectively, the directional strain-rate enhancement factors \\(E_{ij}\\) are defined as the \\(({\\bf e}_i, {\\bf e}_j)\\) -components of \\({\\bf D}\\) relative to that of the rheology in the isotropic limit (isotropic CPO): \\[ E_{ij} = \\frac{ {\\bf e}_i \\cdot {\\bf D}({\\bf S}) \\cdot {\\bf e}_j }{ {\\bf e}_i \\cdot {\\bf D}_{\\mathrm{iso}}({\\bf S}) \\cdot {\\bf e}_j } , \\qquad(1) \\] for a stress state aligned with \\(({\\bf e}_i, {\\bf e}_j)\\) : \\[ {\\bf S}({\\bf e}_i, {\\bf e}_j) = \\tau_0 \\begin{cases} {\\bf I} - 3{\\bf e}_i \\otimes {\\bf e}_i \\;\\;\\quad\\quad\\text{if}\\quad i=j \\\\ {\\bf e}_i \\otimes {\\bf e}_j + {\\bf e}_j \\otimes {\\bf e}_i \\quad\\text{if}\\quad i\\neq j \\\\ \\end{cases} . \\] In this way: \\({E_{11}}\\) is the longitudinal strain-rate enhancement along \\({\\bf e}_{1}\\) when subject to compression along \\({\\bf e}_{1}\\) \\({E_{12}}\\) is the \\({\\bf e}_{1}\\) \u2014 \\({\\bf e}_{2}\\) shear strain-rate enhancement when subject to shear in the \\({\\bf e}_{1}\\) \u2014 \\({\\bf e}_{2}\\) plane and so on. Hard or soft \\(E_{ij}>1\\) implies the material response is softened due to fabric (compared to an isotropic CPO), whereas \\(E_{ij}<1\\) implies hardening . Eigenenhancements Eigenenhancements are defined as the enhancement factors w.r.t. the CPO symmetry axes ( \\({\\bf m}_i\\) ): \\[{\\bf e}_i = {\\bf m}_i .\\] These are the enhancements factors needed to specify the viscous anisotropy in bulk rheologies : Transversely isotropic Orthotropic Grain homogenization Calculating \\(E_{ij}\\) using (1) for a given CPO requires an effective rheology that takes the microstructure into account. In the simplest case, polycrystals may be regarded as an ensemble of interactionless grains (monocrystals), subject to either a homogeneous stress field over the polycrystal scale: \\[ {\\bf S}' = {\\bf S} , \\qquad\\qquad \\text{(Sachs's hypothesis)} \\] or a homogeneous stain-rate field: \\[ {\\bf D}' = {\\bf D} , \\qquad\\qquad \\text{(Taylor's hypothesis)} \\] where \\({\\bf S}'\\) and \\({\\bf D}'\\) are the microscopic (grain-scale) stress and strain-rate tensors, respectively. The effective rheology can then be approximated as the ensemble-averaged monocrystal rheology for either case: \\[ {\\bf D}^{\\mathrm{Sachs}} = \\langle {\\bf D}'({\\bf S}') \\rangle = \\langle {\\bf D}'({\\bf S}) \\rangle , \\qquad\\qquad \\text{(Sachs homogenization)} \\] \\[ \\qquad {\\bf D}^{\\mathrm{Taylor}} = \\langle {\\bf S}'({\\bf D}') \\rangle^{-1} = \\langle {\\bf S}'({\\bf D}) \\rangle^{-1} , \\qquad \\text{(Taylor homogenization)} \\] where \\(\\langle \\cdot \\rangle^{-1}\\) inverts the tensorial relationship. If a linear combination of the two homogenizations is considered, equation (1) can be written as \\[ E_{ij} = (1-\\alpha) \\frac{{\\bf e}_i \\cdot {\\bf D}^{\\mathrm{Sachs}}({\\bf S}) \\cdot {\\bf e}_j} {{\\bf e}_i \\cdot {\\bf D}^{\\mathrm{Sachs}}_{\\mathrm{iso}}({\\bf S}) \\cdot {\\bf e}_j } + {\\alpha} \\frac{ {\\bf e}_i \\cdot {\\bf D}^{\\mathrm{Taylor}}({\\bf S}) \\cdot {\\bf e}_j } { {\\bf e}_i \\cdot {\\bf D}^{\\mathrm{Taylor}}_{\\mathrm{iso}}({\\bf S}) \\cdot {\\bf e}_j } , \\] or simply \\[ E_{ij} = (1-\\alpha)E_{ij}^{\\mathrm{Sachs}} + {\\alpha}E_{ij}^{\\mathrm{Taylor}} , \\] where \\(\\alpha\\) is a free parameter. Grain parameters The grain viscous parameters used for homogenization should be understood as the effective values needed to reproduce deformation experiments on polycrystals; they are not the values derived from experiments on single crystals. Transversely isotropic grains Monocrystal Polycrystal If grains are approximately transversely isotropic, the grain rheology can be modelled using the transversely isotropic power-law rheology . This requires specifying the grain eigenenhancements \\(E_{mm}'\\) and \\(E_{mt}'\\) , the power-law exponent \\(n'\\) , and the Taylor\u2014Sachs weight \\(\\alpha\\) . Example for glacier ice For glacier ice, we follow the literature and rename \\[ {\\bf c} = {\\bf m}^\\prime, \\\\ {\\bf a} = {\\bf t}^\\prime. \\] The below code example shows how to calculate \\(E_{ij}\\) given a4 (or nlm ) assuming the grain parameters proposed by Rathmann and Lilien (2021) : import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(8) ### Synthetic unidirectional CPO (all c-axes aligned in z-direction) m = np.array([0,0,1]) a4 = np.einsum('i,j,k,l', m,m,m,m) # 4-times repeated outer product of m nlm = np.zeros((nlm_len), dtype=np.complex64) nlm[:sf.L4len] = sf.a4_to_nlm(a4) # derive corresponding expansion coefficients ### Basis for enhancement factor calculations (e1,e2,e3, eigvals) = sf.frame(nlm, 'e') # enhancement factors are w.r.t. a^(2) basis (i.e. eigenenhancements) #(e1,e2,e3) = np.eye(3) # enhancement factors are w.r.t. Cartesian basis (x,y,z) ### Transversely isotropic monocrystal parameters for ice (Rathmann & Lilien, 2021) n_grain = 1 # power-law exponent: n=1 => linear grain rheology, nonlinear (n>1) is unsupported. Eij_grain = (1, 1e3) # grain eigenenhancements (Ecc,Eca) for compression along c-axis (Ecc) and for shear parallel to basal plane (Eca) alpha = 0.0125 # Taylor--Sachs weight ### Calculate enhancement factors w.r.t. (e1,e2,e3) Eij = sf.Eij_tranisotropic(nlm, e1,e2,e3, Eij_grain,alpha,n_grain) # Eij=(E11,E22,E33,E23,E13,E12) Choosing grain parameters for glacier ice The grain parameters proposed by Rathmann and Lilien (2021) assume a linear viscous ( \\(n'=1\\) ) response and promote the activation of basal glide by making that slip system soft compared to other systems: \\(E_{ca}' > 1\\) , whereas \\(E_{cc}'=1\\) . This reduces the problem to that of picking \\(E_{ca}'\\) and \\(\\alpha\\) , which Rathmann and Lilien (2021) chose such that deformation tests on unidirectional CPOs (perfect single maximum) are approximately reproduced: \\(E_{mt}=10\\) while \\(E_{mt}/E_{pq} \\sim 10^4\\) , where \\(p,q\\) denote directions at \\(45^\\circ\\) to \\({\\bf m}\\) . The effect of choosing alternative \\(E_{ca}'\\) and \\(\\alpha\\) (left plot) on eigenenhancements for different CPO states (right plot) is here shown for combinations of \\(E_{ca}'\\) and \\(\\alpha\\) that fulfill \\(E_{mt}=10\\) for a unidirectional CPO: Clearly, there is a tradeoff between how shear enhanced ( \\(E_{mt}\\) ) and how hard for axial compression ( \\(E_{mm}\\) ) the model allows a unidirectional CPO to be. Evolving CPO The below animation shows the directional enhancement factors for a CPO evolving under uniaxial compression along \\({\\hat {\\bf z}}\\) when subject to lattice rotation . Enhancement factors are calculated w.r.t. the spherical coordinate basis vectors \\(({\\bf e}_1, {\\bf e}_2, {\\bf e}_3) = ({\\hat{\\bf r}},{\\hat{\\boldsymbol \\theta}},{\\hat{\\boldsymbol \\phi}})\\) . Orthotropic grains Monocrystal Polycrystal If grains are approximately orthotropic, the grain rheology can be modelled using the orthotropic power-law rheology . This requires specifying the grain eigenenhancements \\(E_{ij}'\\) , the power-law exponent \\(n'\\) , and the Taylor\u2014Sachs weight \\(\\alpha\\) . Example for olivine Not yet available.","title":"Strain-rate enhancements"},{"location":"enhancements-strainrate/#strain-rate-enhancements","text":"Given an anisotropic rheology \\({\\bf D}({\\bf S})\\) , where \\({\\bf D}\\) and \\({\\bf S}\\) are the strain-rate and deviatoric stress tensors, respectively, the directional strain-rate enhancement factors \\(E_{ij}\\) are defined as the \\(({\\bf e}_i, {\\bf e}_j)\\) -components of \\({\\bf D}\\) relative to that of the rheology in the isotropic limit (isotropic CPO): \\[ E_{ij} = \\frac{ {\\bf e}_i \\cdot {\\bf D}({\\bf S}) \\cdot {\\bf e}_j }{ {\\bf e}_i \\cdot {\\bf D}_{\\mathrm{iso}}({\\bf S}) \\cdot {\\bf e}_j } , \\qquad(1) \\] for a stress state aligned with \\(({\\bf e}_i, {\\bf e}_j)\\) : \\[ {\\bf S}({\\bf e}_i, {\\bf e}_j) = \\tau_0 \\begin{cases} {\\bf I} - 3{\\bf e}_i \\otimes {\\bf e}_i \\;\\;\\quad\\quad\\text{if}\\quad i=j \\\\ {\\bf e}_i \\otimes {\\bf e}_j + {\\bf e}_j \\otimes {\\bf e}_i \\quad\\text{if}\\quad i\\neq j \\\\ \\end{cases} . \\] In this way: \\({E_{11}}\\) is the longitudinal strain-rate enhancement along \\({\\bf e}_{1}\\) when subject to compression along \\({\\bf e}_{1}\\) \\({E_{12}}\\) is the \\({\\bf e}_{1}\\) \u2014 \\({\\bf e}_{2}\\) shear strain-rate enhancement when subject to shear in the \\({\\bf e}_{1}\\) \u2014 \\({\\bf e}_{2}\\) plane and so on. Hard or soft \\(E_{ij}>1\\) implies the material response is softened due to fabric (compared to an isotropic CPO), whereas \\(E_{ij}<1\\) implies hardening .","title":"Strain-rate enhancements"},{"location":"enhancements-strainrate/#eigenenhancements","text":"Eigenenhancements are defined as the enhancement factors w.r.t. the CPO symmetry axes ( \\({\\bf m}_i\\) ): \\[{\\bf e}_i = {\\bf m}_i .\\] These are the enhancements factors needed to specify the viscous anisotropy in bulk rheologies : Transversely isotropic Orthotropic","title":"Eigenenhancements"},{"location":"enhancements-strainrate/#grain-homogenization","text":"Calculating \\(E_{ij}\\) using (1) for a given CPO requires an effective rheology that takes the microstructure into account. In the simplest case, polycrystals may be regarded as an ensemble of interactionless grains (monocrystals), subject to either a homogeneous stress field over the polycrystal scale: \\[ {\\bf S}' = {\\bf S} , \\qquad\\qquad \\text{(Sachs's hypothesis)} \\] or a homogeneous stain-rate field: \\[ {\\bf D}' = {\\bf D} , \\qquad\\qquad \\text{(Taylor's hypothesis)} \\] where \\({\\bf S}'\\) and \\({\\bf D}'\\) are the microscopic (grain-scale) stress and strain-rate tensors, respectively. The effective rheology can then be approximated as the ensemble-averaged monocrystal rheology for either case: \\[ {\\bf D}^{\\mathrm{Sachs}} = \\langle {\\bf D}'({\\bf S}') \\rangle = \\langle {\\bf D}'({\\bf S}) \\rangle , \\qquad\\qquad \\text{(Sachs homogenization)} \\] \\[ \\qquad {\\bf D}^{\\mathrm{Taylor}} = \\langle {\\bf S}'({\\bf D}') \\rangle^{-1} = \\langle {\\bf S}'({\\bf D}) \\rangle^{-1} , \\qquad \\text{(Taylor homogenization)} \\] where \\(\\langle \\cdot \\rangle^{-1}\\) inverts the tensorial relationship. If a linear combination of the two homogenizations is considered, equation (1) can be written as \\[ E_{ij} = (1-\\alpha) \\frac{{\\bf e}_i \\cdot {\\bf D}^{\\mathrm{Sachs}}({\\bf S}) \\cdot {\\bf e}_j} {{\\bf e}_i \\cdot {\\bf D}^{\\mathrm{Sachs}}_{\\mathrm{iso}}({\\bf S}) \\cdot {\\bf e}_j } + {\\alpha} \\frac{ {\\bf e}_i \\cdot {\\bf D}^{\\mathrm{Taylor}}({\\bf S}) \\cdot {\\bf e}_j } { {\\bf e}_i \\cdot {\\bf D}^{\\mathrm{Taylor}}_{\\mathrm{iso}}({\\bf S}) \\cdot {\\bf e}_j } , \\] or simply \\[ E_{ij} = (1-\\alpha)E_{ij}^{\\mathrm{Sachs}} + {\\alpha}E_{ij}^{\\mathrm{Taylor}} , \\] where \\(\\alpha\\) is a free parameter. Grain parameters The grain viscous parameters used for homogenization should be understood as the effective values needed to reproduce deformation experiments on polycrystals; they are not the values derived from experiments on single crystals.","title":"Grain homogenization"},{"location":"enhancements-strainrate/#transversely-isotropic-grains","text":"Monocrystal Polycrystal If grains are approximately transversely isotropic, the grain rheology can be modelled using the transversely isotropic power-law rheology . This requires specifying the grain eigenenhancements \\(E_{mm}'\\) and \\(E_{mt}'\\) , the power-law exponent \\(n'\\) , and the Taylor\u2014Sachs weight \\(\\alpha\\) .","title":"Transversely isotropic grains"},{"location":"enhancements-strainrate/#example-for-glacier-ice","text":"For glacier ice, we follow the literature and rename \\[ {\\bf c} = {\\bf m}^\\prime, \\\\ {\\bf a} = {\\bf t}^\\prime. \\] The below code example shows how to calculate \\(E_{ij}\\) given a4 (or nlm ) assuming the grain parameters proposed by Rathmann and Lilien (2021) : import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(8) ### Synthetic unidirectional CPO (all c-axes aligned in z-direction) m = np.array([0,0,1]) a4 = np.einsum('i,j,k,l', m,m,m,m) # 4-times repeated outer product of m nlm = np.zeros((nlm_len), dtype=np.complex64) nlm[:sf.L4len] = sf.a4_to_nlm(a4) # derive corresponding expansion coefficients ### Basis for enhancement factor calculations (e1,e2,e3, eigvals) = sf.frame(nlm, 'e') # enhancement factors are w.r.t. a^(2) basis (i.e. eigenenhancements) #(e1,e2,e3) = np.eye(3) # enhancement factors are w.r.t. Cartesian basis (x,y,z) ### Transversely isotropic monocrystal parameters for ice (Rathmann & Lilien, 2021) n_grain = 1 # power-law exponent: n=1 => linear grain rheology, nonlinear (n>1) is unsupported. Eij_grain = (1, 1e3) # grain eigenenhancements (Ecc,Eca) for compression along c-axis (Ecc) and for shear parallel to basal plane (Eca) alpha = 0.0125 # Taylor--Sachs weight ### Calculate enhancement factors w.r.t. (e1,e2,e3) Eij = sf.Eij_tranisotropic(nlm, e1,e2,e3, Eij_grain,alpha,n_grain) # Eij=(E11,E22,E33,E23,E13,E12) Choosing grain parameters for glacier ice The grain parameters proposed by Rathmann and Lilien (2021) assume a linear viscous ( \\(n'=1\\) ) response and promote the activation of basal glide by making that slip system soft compared to other systems: \\(E_{ca}' > 1\\) , whereas \\(E_{cc}'=1\\) . This reduces the problem to that of picking \\(E_{ca}'\\) and \\(\\alpha\\) , which Rathmann and Lilien (2021) chose such that deformation tests on unidirectional CPOs (perfect single maximum) are approximately reproduced: \\(E_{mt}=10\\) while \\(E_{mt}/E_{pq} \\sim 10^4\\) , where \\(p,q\\) denote directions at \\(45^\\circ\\) to \\({\\bf m}\\) . The effect of choosing alternative \\(E_{ca}'\\) and \\(\\alpha\\) (left plot) on eigenenhancements for different CPO states (right plot) is here shown for combinations of \\(E_{ca}'\\) and \\(\\alpha\\) that fulfill \\(E_{mt}=10\\) for a unidirectional CPO: Clearly, there is a tradeoff between how shear enhanced ( \\(E_{mt}\\) ) and how hard for axial compression ( \\(E_{mm}\\) ) the model allows a unidirectional CPO to be. Evolving CPO The below animation shows the directional enhancement factors for a CPO evolving under uniaxial compression along \\({\\hat {\\bf z}}\\) when subject to lattice rotation . Enhancement factors are calculated w.r.t. the spherical coordinate basis vectors \\(({\\bf e}_1, {\\bf e}_2, {\\bf e}_3) = ({\\hat{\\bf r}},{\\hat{\\boldsymbol \\theta}},{\\hat{\\boldsymbol \\phi}})\\) .","title":"Example for glacier ice"},{"location":"enhancements-strainrate/#orthotropic-grains","text":"Monocrystal Polycrystal If grains are approximately orthotropic, the grain rheology can be modelled using the orthotropic power-law rheology . This requires specifying the grain eigenenhancements \\(E_{ij}'\\) , the power-law exponent \\(n'\\) , and the Taylor\u2014Sachs weight \\(\\alpha\\) .","title":"Orthotropic grains"},{"location":"enhancements-strainrate/#example-for-olivine","text":"Not yet available.","title":"Example for olivine"},{"location":"plotting/","text":"Plotting CPO The orientation distribution function (ODF; normalized expansion series) can be plotted as follows: import numpy as np import matplotlib.pyplot as plt from specfabpy import specfab as sf from specfabpy import plotting as sfplt lm, nlm_len = sf.init(6) ### CPO to be plotted a2 = np.diag([0,0,1]) # CPO characterized by a^(2) nlm = sf.a2_to_nlm(a2) # vector of expansion coefficients ### Setup axes and projection geo, prj = sfplt.getprojection(rotation=45, inclination=45) fig = plt.figure(figsize=(2,2)) ax = plt.subplot(111, projection=prj) ax.set_global() # ensure entire S^2 is shown ### Plot lvlset = 'iso-up' # default level set: lowest tick/level is the value of an isotropic distribution lvlset = (np.linspace(0,0.8,9), lambda x,p:'%.1f'%x) # custom level set: (list of levels, how to format colorbar tick labels) sfplt.plotODF(nlm, lm, ax, cmap='Greys', lvlset=lvlset) # plot distribution (see src/specfabpy/plotting.py for API) sfplt.plotcoordaxes(ax, geo, color=sfplt.c_dred) # plot coordinate axes (see src/specfabpy/plotting.py for API) plt.savefig('ODF-plot.png', dpi=175, pad_inches=0.1, bbox_inches='tight') Parcel deformation Given a deformation gradient \\({\\bf F}\\) , the effect on an undeformed parcel can be plotting following: import numpy as np import matplotlib.pyplot as plt from specfabpy import specfab as sf from specfabpy import plotting as sfplt lm, nlm_len = sf.init(6) ### Determine deformation gradient F # Pure shear axis = 2 # axis of compression/extension (0=x, 1=y, 2=z) r = 0 # deformation asymmetry T_ps = 1 # e-folding time scale t_ps = 1 # time at which deformed parcel is sought F_ps = sf.pureshear_F(axis, r, T_ps, t_ps) # deformation gradient tensor # Simple shear plane = 1 # shear plane (0=yz, 1=xz, 2=xy) T_ss = 1 # characteristic time taken to reach shear strain 45 deg. t_ss = 1 # time at which deformed parcel is sought F_ss = sf.simpleshear_F(plane, T_ss, t_ss) # deformation gradient tensor ### Plot fig = plt.figure(figsize=(6,6)) ax1 = plt.subplot(121, projection='3d') ax2 = plt.subplot(122, projection='3d') sfplt.plotparcel(ax1, F_ps, azim=35, axscale=1.7, axislabels=True, drawinit=True) sfplt.plotparcel(ax2, F_ss, azim=35, axscale=1.7, axislabels=True, drawinit=True) ax1.set_title(r'$\\epsilon_{zz}=%.2f$'%(sf.F_to_strain(F_ps)[2,2])) ax2.set_title(r'$\\gamma=%.0f$ deg.'%(np.rad2deg(sf.simpleshear_gamma(T_ss, t_ss)))) plt.savefig('deformed-parcel.png', dpi=175, pad_inches=0.1, bbox_inches='tight')","title":"Plotting"},{"location":"plotting/#plotting","text":"","title":"Plotting"},{"location":"plotting/#cpo","text":"The orientation distribution function (ODF; normalized expansion series) can be plotted as follows: import numpy as np import matplotlib.pyplot as plt from specfabpy import specfab as sf from specfabpy import plotting as sfplt lm, nlm_len = sf.init(6) ### CPO to be plotted a2 = np.diag([0,0,1]) # CPO characterized by a^(2) nlm = sf.a2_to_nlm(a2) # vector of expansion coefficients ### Setup axes and projection geo, prj = sfplt.getprojection(rotation=45, inclination=45) fig = plt.figure(figsize=(2,2)) ax = plt.subplot(111, projection=prj) ax.set_global() # ensure entire S^2 is shown ### Plot lvlset = 'iso-up' # default level set: lowest tick/level is the value of an isotropic distribution lvlset = (np.linspace(0,0.8,9), lambda x,p:'%.1f'%x) # custom level set: (list of levels, how to format colorbar tick labels) sfplt.plotODF(nlm, lm, ax, cmap='Greys', lvlset=lvlset) # plot distribution (see src/specfabpy/plotting.py for API) sfplt.plotcoordaxes(ax, geo, color=sfplt.c_dred) # plot coordinate axes (see src/specfabpy/plotting.py for API) plt.savefig('ODF-plot.png', dpi=175, pad_inches=0.1, bbox_inches='tight')","title":"CPO"},{"location":"plotting/#parcel-deformation","text":"Given a deformation gradient \\({\\bf F}\\) , the effect on an undeformed parcel can be plotting following: import numpy as np import matplotlib.pyplot as plt from specfabpy import specfab as sf from specfabpy import plotting as sfplt lm, nlm_len = sf.init(6) ### Determine deformation gradient F # Pure shear axis = 2 # axis of compression/extension (0=x, 1=y, 2=z) r = 0 # deformation asymmetry T_ps = 1 # e-folding time scale t_ps = 1 # time at which deformed parcel is sought F_ps = sf.pureshear_F(axis, r, T_ps, t_ps) # deformation gradient tensor # Simple shear plane = 1 # shear plane (0=yz, 1=xz, 2=xy) T_ss = 1 # characteristic time taken to reach shear strain 45 deg. t_ss = 1 # time at which deformed parcel is sought F_ss = sf.simpleshear_F(plane, T_ss, t_ss) # deformation gradient tensor ### Plot fig = plt.figure(figsize=(6,6)) ax1 = plt.subplot(121, projection='3d') ax2 = plt.subplot(122, projection='3d') sfplt.plotparcel(ax1, F_ps, azim=35, axscale=1.7, axislabels=True, drawinit=True) sfplt.plotparcel(ax2, F_ss, azim=35, axscale=1.7, axislabels=True, drawinit=True) ax1.set_title(r'$\\epsilon_{zz}=%.2f$'%(sf.F_to_strain(F_ps)[2,2])) ax2.set_title(r'$\\gamma=%.0f$ deg.'%(np.rad2deg(sf.simpleshear_gamma(T_ss, t_ss)))) plt.savefig('deformed-parcel.png', dpi=175, pad_inches=0.1, bbox_inches='tight')","title":"Parcel deformation"},{"location":"radar-derived-PP/","text":"Radar-derived physical properties of glacier ice Introduction The dielectric permittivity tensor of a single ice crystal is approximately transversely isotropic w.r.t. the crystal \\(c\\) -axis: $$ \\epsilon_{ij}' = (2\\epsilon_{\\perp}' + \\epsilon_{\\parallel}') \\frac{\\delta_{ij}}{3} + (\\epsilon_{\\parallel}'-\\epsilon_{\\perp}') \\left(c_i c_j - \\frac{\\delta_{ij}}{3} \\right), $$ where \\(\\epsilon_{\\parallel}'\\) and \\(\\epsilon_{\\perp}'\\) are the components parallel and perpendicular to the \\(c\\) -axis, respectively, which depend on ice temperature and EM-wave frequency ( Fujita et al., 2000 ). For wave lengths much longer than the average grain size, the bulk permittivity tensor of polycrystalline ice may be approximated as the grain-average permittivity tensor, \\(\\epsilon_{ij} \\simeq \\langle \\epsilon_{ij}' \\rangle\\) , constructed by averaging over all grain orientations (over the CPO) assuming grain sizes are uncorrelated with orientation: \\[ \\langle \\epsilon_{ij}' \\rangle = (2\\epsilon_{\\perp}' + \\epsilon_{\\parallel}') \\frac{\\delta_{ij}}{3} + (\\epsilon_{\\parallel}'-\\epsilon_{\\perp}') \\left(\\langle c_i c_j \\rangle - \\frac{\\delta_{ij}}{3} \\right) , \\] where \\(\\langle c_i c_j \\rangle\\) is the second-order structure tensor , defined as \\[ \\langle c_i c_j \\rangle = \\frac{1}{N}\\sum_{k=1}^N { c_i^{(k)} c_j^{(k)} }. \\] Thus, because the bulk permittivity tensor \\(\\epsilon_{ij}\\) can be inferred from EM-wave speeds and radar return-power anomalies, so can \\(\\langle c_i c_j \\rangle\\) . Radar measurements \\(\\rightarrow\\) CPO A useful approximation over large parts of ice sheets is that \\(\\langle c_i c_j \\rangle\\) has a vertical eigenvector, in which case the Cartesian components are \\[ \\langle c_i c_j \\rangle = \\left[\\begin{matrix} a_{xx} & a_{xy} & 0\\\\ a_{xy} & a_{yy} & 0\\\\ 0 & 0 & a_{zz} \\end{matrix}\\right] . \\] Let us consider the usual case where the difference in horizontal eigenvalues of \\(\\langle c_i c_j \\rangle\\) , \\[ \\Delta \\lambda = \\lambda_2 - \\lambda_1, \\] can be inferred from ice-penetrating radar, where \\({\\bf m}_1\\) and \\({\\bf m}_2\\) are the corresponding horizontal eigenvectors and eigenvalues are sorted such that \\(\\lambda_1 \\leq \\lambda_2\\) . It follows that the structure tensor, posed in its eigenframe ( \\({\\bf m}_1, {\\bf m}_2, {\\bf z}\\) ), is \\[ \\langle c_i c_j \\rangle = \\left[\\begin{matrix} \\lambda_1 & 0 & 0 \\\\ 0 & \\lambda_1 + \\Delta\\lambda & 0 \\\\ 0 & 0 & 1 - \\Delta \\lambda - 2\\lambda_1 \\end{matrix}\\right] , \\] where the identity \\(\\operatorname{tr}(\\langle c_i c_j \\rangle) = 1\\) was used. Gerber's approximation Since \\(\\lambda_1\\) is unknown, the problem can be closed by making different assumptions about \\(\\lambda_1\\) given the local/upstream flow regime, such as proposed by Gerber et al. (2023) . Suppose \\(\\Delta\\lambda\\) is measured in region where \\(c\\) -axes are, to a good approximation, suspected to be distributed on the \\({\\bf m}_2\\) \u2014 \\({\\bf z}\\) plane because the smallest eigenvalue is vanishing, \\(\\lambda_1 \\rightarrow 0\\) . In this case, \\(\\Delta \\lambda = 0\\) represents a perfect single-maximum along \\({\\bf z}\\) , \\(\\Delta \\lambda = 0.5\\) a perfect girdle in the \\({\\bf m}_2\\) \u2014 \\({\\bf z}\\) plane, and \\(\\Delta \\lambda = 1\\) a perfect single-maximum along \\({\\bf m}_2\\) , respectively: CPO \\(\\rightarrow\\) Enhancement factors If \\(\\langle c_i c_j \\rangle\\) can be inferred from radar sounding following the above method, so can the bulk strain-rate enhancement factors, \\(E_{ij}\\) , in the same eigenframe (i.e. eigenenhancements ). The eigenenhancements depend, however, also on the fourth-order structure tensor, \\(\\langle c_i c_j c_k c_l \\rangle\\) , but the bulk permittivity \\(\\epsilon_{ij}\\) is insensitive to \\(\\langle c_i c_j c_k c_l \\rangle\\) . To overcome this, a simple empirical correlation is adopted that allows determining \\(\\langle c_i c_j c_k c_l \\rangle\\) given \\(\\langle c_i c_j\\rangle\\) if the CPO is approximately rotationally symmetric. Correlation between \\(\\langle c_i c_j c_k c_l \\rangle\\) and \\(\\langle c_i c_j\\rangle\\) If the CPO symmetry axis is rotated into the vertical direction, \\(\\langle c_i c_j\\rangle\\) depends only on the normalized spectral component \\(\\hat{n}_2^0 = n_2^0/n_0^0:\\) \\[ \\langle c_i c_j\\rangle = \\frac{\\delta_{ij}}{3} + \\frac{2\\sqrt{5}}{15} \\hat{n}_2^0 \\left[\\begin{matrix} -1/2 & 0 & 0 \\\\ 0 & -1/2 & 0 \\\\ 0 & 0 & 1 \\end{matrix}\\right] , \\] and \\(\\langle c_i c_j c_k c_l \\rangle\\) only on \\(\\hat{n}_2^0\\) and \\(\\hat{n}_4^0 = n_4^0/n_0^0\\) (not shown). The figure below shows the empirical correlation between these two components based on ice-core samples. Thus, if \\(\\hat{n}_2^0\\) is extracted from \\(\\langle c_i c_j\\rangle\\) in this frame, \\(\\hat{n}_4^0\\) can be derived and hence \\(\\langle c_i c_j c_k c_l \\rangle\\) constructed. To pose the CPO in the original, unrotated eigenframe ( \\({\\bf m}_1, {\\bf m}_2, {\\bf z}\\) ), the resulting expansion series is finally rotated back, allowing eigenenhancements to easily be calculated using specfab. Code example The following code demonstrates how to take each step with specfab: import numpy as np from scipy.spatial.transform import Rotation from specfabpy import specfab as sf lm, nlm_len = sf.init(4) # L=4 is sufficient here ### Determine from radar-derived Delta lambda l1 = 0 # lambda_1 = 0 (Gerber's approximation) dl = 0.5 # Delta lambda = lambda_2 - lambda_1 a2 = np.diag([l1, l1+dl, 1-dl-2*l1]) # second-order structure tensor, , in eigenframe m1, m2, z = np.array([1,0,0]), np.array([0,1,0]), np.array([0,0,1]) # eigenvectors ### Rotate into a rotationally-symmetric frame about z Rm1 = Rotation.from_rotvec(np.pi/2 * m1).as_matrix() # Rotate 90 deg about m1 eigenvector Rm2 = Rotation.from_rotvec(np.pi/2 * m2).as_matrix() # Rotate 90 deg about m2 eigenvector if dl < 0.4: a2_vs = a2 # Already in rotationally-symmetric frame about z if 0.4 <= dl <= 0.6: a2_vs = np.matmul(Rm2,np.matmul(a2,Rm2.T)) # Rotate vertical (m2--z) girdle into horizontal (m1--m2) girdle if dl > 0.6: a2_vs = np.matmul(Rm1,np.matmul(a2,Rm1.T)) # Rotate horizontal (m2) single-maximum into vertical (z) single-maximum ### Determine \\hat{n}_4^0 (= n_4^0/n_0^0) from \\hat{n}_2^0 (= n_2^0/n_0^0) in rotationally-symmetric frame about z nhat20 = (a2_vs[2,2]- 1/3)/(2/15*np.sqrt(5)) # azz -> nhat20 nhat40 = sf.nhat40_empcorr_ice(nhat20)[0] ### Construct nlm (spectral CPO state vector) in rotationally-symmetric frame about z nlm_vs = np.zeros(nlm_len, dtype=np.complex128) n00 = 1/np.sqrt(4*np.pi) # only grain-number normalized distribution is known, so must integrate to 1 over S^2. nlm_vs[0] = n00 nlm_vs[3] = nhat20*n00 nlm_vs[10] = nhat40*n00 ### Rotate spectral CPO state back to origional (m1,m2,z) eigenframe if dl < 0.4: nlm = nlm_vs # Already in vertical symmetric frame if 0.4 <= dl <= 0.6: nlm = sf.rotate_nlm(nlm_vs, -np.pi/2, 0) # Rotate horizontal (m1--m2) girdle back into vertical (m2--z) girdle if dl > 0.6: nlm = sf.rotate_nlm(sf.rotate_nlm(nlm_vs, -np.pi/2, 0), 0 ,-np.pi/2) # Rotate vertical (z) single-maximum back into horizontal (m2) single-maximum ### Calculate eigenenhancements # Transversely isotropic monocrystal parameters for ice (Rathmann & Lilien, 2021) n_grain = 1 # Power-law exponent: n=1 => linear grain rheology, nonlinear (n>1) is unsupported Eij_grain = (1, 1e3) # Grain eigenenhancements (Ecc,Eca) for compression along c-axis (Ecc) and for shear parallel to basal plane (Eca) alpha = 0.0125 # Taylor--Sachs weight # Tuple of eigenenhancements (bulk enhancement factors w.r.t. m1, m2, z) e1, e2, e3 = m1, m2, z Eij = sf.Eij_tranisotropic(nlm, e1,e2,e3, Eij_grain,alpha,n_grain) # (E_{m1,m1},E_{m2,m2},E_{zz},E_{m2,z),E_{m1,z},E_{m1,m2}) # To calculate bulk enhancement factors w.r.t. other axes of deformation/stress, change (e1,e2,e3) accordingly. For reference, the below plots show the different CPOs at each step for \\(\\Delta\\lambda=0.5\\) and \\(\\Delta\\lambda=1\\) . \\(\\Delta\\lambda = 0.5\\) \\(\\Delta\\lambda = 1.0\\)","title":"Radar-derived physical properties"},{"location":"radar-derived-PP/#radar-derived-physical-properties-of-glacier-ice","text":"","title":"Radar-derived physical properties of glacier ice"},{"location":"radar-derived-PP/#introduction","text":"The dielectric permittivity tensor of a single ice crystal is approximately transversely isotropic w.r.t. the crystal \\(c\\) -axis: $$ \\epsilon_{ij}' = (2\\epsilon_{\\perp}' + \\epsilon_{\\parallel}') \\frac{\\delta_{ij}}{3} + (\\epsilon_{\\parallel}'-\\epsilon_{\\perp}') \\left(c_i c_j - \\frac{\\delta_{ij}}{3} \\right), $$ where \\(\\epsilon_{\\parallel}'\\) and \\(\\epsilon_{\\perp}'\\) are the components parallel and perpendicular to the \\(c\\) -axis, respectively, which depend on ice temperature and EM-wave frequency ( Fujita et al., 2000 ). For wave lengths much longer than the average grain size, the bulk permittivity tensor of polycrystalline ice may be approximated as the grain-average permittivity tensor, \\(\\epsilon_{ij} \\simeq \\langle \\epsilon_{ij}' \\rangle\\) , constructed by averaging over all grain orientations (over the CPO) assuming grain sizes are uncorrelated with orientation: \\[ \\langle \\epsilon_{ij}' \\rangle = (2\\epsilon_{\\perp}' + \\epsilon_{\\parallel}') \\frac{\\delta_{ij}}{3} + (\\epsilon_{\\parallel}'-\\epsilon_{\\perp}') \\left(\\langle c_i c_j \\rangle - \\frac{\\delta_{ij}}{3} \\right) , \\] where \\(\\langle c_i c_j \\rangle\\) is the second-order structure tensor , defined as \\[ \\langle c_i c_j \\rangle = \\frac{1}{N}\\sum_{k=1}^N { c_i^{(k)} c_j^{(k)} }. \\] Thus, because the bulk permittivity tensor \\(\\epsilon_{ij}\\) can be inferred from EM-wave speeds and radar return-power anomalies, so can \\(\\langle c_i c_j \\rangle\\) .","title":"Introduction"},{"location":"radar-derived-PP/#radar-measurements-rightarrow-cpo","text":"A useful approximation over large parts of ice sheets is that \\(\\langle c_i c_j \\rangle\\) has a vertical eigenvector, in which case the Cartesian components are \\[ \\langle c_i c_j \\rangle = \\left[\\begin{matrix} a_{xx} & a_{xy} & 0\\\\ a_{xy} & a_{yy} & 0\\\\ 0 & 0 & a_{zz} \\end{matrix}\\right] . \\] Let us consider the usual case where the difference in horizontal eigenvalues of \\(\\langle c_i c_j \\rangle\\) , \\[ \\Delta \\lambda = \\lambda_2 - \\lambda_1, \\] can be inferred from ice-penetrating radar, where \\({\\bf m}_1\\) and \\({\\bf m}_2\\) are the corresponding horizontal eigenvectors and eigenvalues are sorted such that \\(\\lambda_1 \\leq \\lambda_2\\) . It follows that the structure tensor, posed in its eigenframe ( \\({\\bf m}_1, {\\bf m}_2, {\\bf z}\\) ), is \\[ \\langle c_i c_j \\rangle = \\left[\\begin{matrix} \\lambda_1 & 0 & 0 \\\\ 0 & \\lambda_1 + \\Delta\\lambda & 0 \\\\ 0 & 0 & 1 - \\Delta \\lambda - 2\\lambda_1 \\end{matrix}\\right] , \\] where the identity \\(\\operatorname{tr}(\\langle c_i c_j \\rangle) = 1\\) was used.","title":"Radar measurements \\(\\rightarrow\\) CPO"},{"location":"radar-derived-PP/#gerbers-approximation","text":"Since \\(\\lambda_1\\) is unknown, the problem can be closed by making different assumptions about \\(\\lambda_1\\) given the local/upstream flow regime, such as proposed by Gerber et al. (2023) . Suppose \\(\\Delta\\lambda\\) is measured in region where \\(c\\) -axes are, to a good approximation, suspected to be distributed on the \\({\\bf m}_2\\) \u2014 \\({\\bf z}\\) plane because the smallest eigenvalue is vanishing, \\(\\lambda_1 \\rightarrow 0\\) . In this case, \\(\\Delta \\lambda = 0\\) represents a perfect single-maximum along \\({\\bf z}\\) , \\(\\Delta \\lambda = 0.5\\) a perfect girdle in the \\({\\bf m}_2\\) \u2014 \\({\\bf z}\\) plane, and \\(\\Delta \\lambda = 1\\) a perfect single-maximum along \\({\\bf m}_2\\) , respectively:","title":"Gerber's approximation"},{"location":"radar-derived-PP/#cpo-rightarrow-enhancement-factors","text":"If \\(\\langle c_i c_j \\rangle\\) can be inferred from radar sounding following the above method, so can the bulk strain-rate enhancement factors, \\(E_{ij}\\) , in the same eigenframe (i.e. eigenenhancements ). The eigenenhancements depend, however, also on the fourth-order structure tensor, \\(\\langle c_i c_j c_k c_l \\rangle\\) , but the bulk permittivity \\(\\epsilon_{ij}\\) is insensitive to \\(\\langle c_i c_j c_k c_l \\rangle\\) . To overcome this, a simple empirical correlation is adopted that allows determining \\(\\langle c_i c_j c_k c_l \\rangle\\) given \\(\\langle c_i c_j\\rangle\\) if the CPO is approximately rotationally symmetric.","title":"CPO \\(\\rightarrow\\) Enhancement factors"},{"location":"radar-derived-PP/#correlation-between-langle-c_i-c_j-c_k-c_l-rangle-and-langle-c_i-c_jrangle","text":"If the CPO symmetry axis is rotated into the vertical direction, \\(\\langle c_i c_j\\rangle\\) depends only on the normalized spectral component \\(\\hat{n}_2^0 = n_2^0/n_0^0:\\) \\[ \\langle c_i c_j\\rangle = \\frac{\\delta_{ij}}{3} + \\frac{2\\sqrt{5}}{15} \\hat{n}_2^0 \\left[\\begin{matrix} -1/2 & 0 & 0 \\\\ 0 & -1/2 & 0 \\\\ 0 & 0 & 1 \\end{matrix}\\right] , \\] and \\(\\langle c_i c_j c_k c_l \\rangle\\) only on \\(\\hat{n}_2^0\\) and \\(\\hat{n}_4^0 = n_4^0/n_0^0\\) (not shown). The figure below shows the empirical correlation between these two components based on ice-core samples. Thus, if \\(\\hat{n}_2^0\\) is extracted from \\(\\langle c_i c_j\\rangle\\) in this frame, \\(\\hat{n}_4^0\\) can be derived and hence \\(\\langle c_i c_j c_k c_l \\rangle\\) constructed. To pose the CPO in the original, unrotated eigenframe ( \\({\\bf m}_1, {\\bf m}_2, {\\bf z}\\) ), the resulting expansion series is finally rotated back, allowing eigenenhancements to easily be calculated using specfab.","title":"Correlation between \\(\\langle c_i c_j c_k c_l \\rangle\\) and \\(\\langle c_i c_j\\rangle\\)"},{"location":"radar-derived-PP/#code-example","text":"The following code demonstrates how to take each step with specfab: import numpy as np from scipy.spatial.transform import Rotation from specfabpy import specfab as sf lm, nlm_len = sf.init(4) # L=4 is sufficient here ### Determine from radar-derived Delta lambda l1 = 0 # lambda_1 = 0 (Gerber's approximation) dl = 0.5 # Delta lambda = lambda_2 - lambda_1 a2 = np.diag([l1, l1+dl, 1-dl-2*l1]) # second-order structure tensor, , in eigenframe m1, m2, z = np.array([1,0,0]), np.array([0,1,0]), np.array([0,0,1]) # eigenvectors ### Rotate into a rotationally-symmetric frame about z Rm1 = Rotation.from_rotvec(np.pi/2 * m1).as_matrix() # Rotate 90 deg about m1 eigenvector Rm2 = Rotation.from_rotvec(np.pi/2 * m2).as_matrix() # Rotate 90 deg about m2 eigenvector if dl < 0.4: a2_vs = a2 # Already in rotationally-symmetric frame about z if 0.4 <= dl <= 0.6: a2_vs = np.matmul(Rm2,np.matmul(a2,Rm2.T)) # Rotate vertical (m2--z) girdle into horizontal (m1--m2) girdle if dl > 0.6: a2_vs = np.matmul(Rm1,np.matmul(a2,Rm1.T)) # Rotate horizontal (m2) single-maximum into vertical (z) single-maximum ### Determine \\hat{n}_4^0 (= n_4^0/n_0^0) from \\hat{n}_2^0 (= n_2^0/n_0^0) in rotationally-symmetric frame about z nhat20 = (a2_vs[2,2]- 1/3)/(2/15*np.sqrt(5)) # azz -> nhat20 nhat40 = sf.nhat40_empcorr_ice(nhat20)[0] ### Construct nlm (spectral CPO state vector) in rotationally-symmetric frame about z nlm_vs = np.zeros(nlm_len, dtype=np.complex128) n00 = 1/np.sqrt(4*np.pi) # only grain-number normalized distribution is known, so must integrate to 1 over S^2. nlm_vs[0] = n00 nlm_vs[3] = nhat20*n00 nlm_vs[10] = nhat40*n00 ### Rotate spectral CPO state back to origional (m1,m2,z) eigenframe if dl < 0.4: nlm = nlm_vs # Already in vertical symmetric frame if 0.4 <= dl <= 0.6: nlm = sf.rotate_nlm(nlm_vs, -np.pi/2, 0) # Rotate horizontal (m1--m2) girdle back into vertical (m2--z) girdle if dl > 0.6: nlm = sf.rotate_nlm(sf.rotate_nlm(nlm_vs, -np.pi/2, 0), 0 ,-np.pi/2) # Rotate vertical (z) single-maximum back into horizontal (m2) single-maximum ### Calculate eigenenhancements # Transversely isotropic monocrystal parameters for ice (Rathmann & Lilien, 2021) n_grain = 1 # Power-law exponent: n=1 => linear grain rheology, nonlinear (n>1) is unsupported Eij_grain = (1, 1e3) # Grain eigenenhancements (Ecc,Eca) for compression along c-axis (Ecc) and for shear parallel to basal plane (Eca) alpha = 0.0125 # Taylor--Sachs weight # Tuple of eigenenhancements (bulk enhancement factors w.r.t. m1, m2, z) e1, e2, e3 = m1, m2, z Eij = sf.Eij_tranisotropic(nlm, e1,e2,e3, Eij_grain,alpha,n_grain) # (E_{m1,m1},E_{m2,m2},E_{zz},E_{m2,z),E_{m1,z},E_{m1,m2}) # To calculate bulk enhancement factors w.r.t. other axes of deformation/stress, change (e1,e2,e3) accordingly. For reference, the below plots show the different CPOs at each step for \\(\\Delta\\lambda=0.5\\) and \\(\\Delta\\lambda=1\\) . \\(\\Delta\\lambda = 0.5\\) \\(\\Delta\\lambda = 1.0\\)","title":"Code example"},{"location":"wavepropagation-elastic/","text":"Elastic wave propagation Problem We seek plane wave solutions of the Cauchy-Navier equation of motion \\[ \\nabla\\cdot {\\boldsymbol \\sigma} = \\rho \\frac{\\partial^2 {\\bf d}}{\\partial t^2}, \\] where \\({\\boldsymbol \\sigma}\\) is the bulk stress tensor, \\({\\bf d}\\) is the displacement field, and \\(\\rho\\) the mass density. Substituting \\({\\bf d}\\) for a plane wave solution, \\({\\bf d} = {\\bf d}_0 \\exp[i({\\bf k}\\cdot {\\bf x} - \\omega t)]\\) , the problem reduces to \\[ (k^2\\hat{{\\bf Q}} - \\omega^2 \\rho {\\bf I}) {\\bf d} = {\\bf 0}, \\] where \\(\\hat{{\\bf Q}}(\\hat{{\\bf k}})\\) is the normalized acoustic tensor that varies depending on the bulk constitutive equation substituted for \\({\\boldsymbol \\sigma}({\\bf d})\\) . The above equation requires \\[ \\det( k^2\\hat{{\\bf Q}} - \\omega^2 \\rho {\\bf I} ) = 0 . \\] Evidently, the eigenvalues and eigenvectors of \\(\\hat{{\\bf Q}}/\\rho\\) are the permitted wave velocities squared and wave polarization, respectively, where \\[ V^2 = \\frac{\\omega^2}{k^2} \\] is the wave velocity squared. Homogenization The problem may be closed by approximating \\(\\hat{{\\bf Q}}\\) as the grain-averaged acoustic tensor, subject to a linear combination of the Voigt and Reuss homogenization schemes: \\[ \\hat{{\\bf Q}} = (1-\\alpha) \\langle\\hat{{\\bf Q}}'_{\\mathrm{Reuss}}\\rangle + \\alpha \\langle \\hat{{\\bf Q}}'_{\\mathrm{Voigt}}\\rangle . \\] The Voigt scheme ( \\(\\alpha=1\\) ) assumes the strain field is homogeneous over the polycrystal scale, whereas the Reuss scheme ( \\(\\alpha=0\\) ) assumes the stress is homogeneous. In these homogenizations, grains are therefore assumed interactionless and the bulk elastic behaviour is therefore simply the grain-orientation-averaged elastic behaviour subject to either homogeneous stress or strain assumptions over the polycrystal scale. Grain parameters The grain elastic parameters used for homogenization should be understood as the effective polycrystal values needed to reproduce experimental results; they are not the values derived from experiments on single crystals. Transversely isotropic grains Monocrystal Polycrystal If grains are approximately transversely isotropic, the grain elastic behaviour can be modelled using the transversely isotropic elastic constitutive equation . This requires specifying the grain elastic parameters \\(\\lambda'\\) , \\(\\mu'\\) , \\(\\hat{\\lambda}'\\) , \\(\\hat{\\mu}'\\) , \\(\\hat{\\gamma}'\\) , and the Voigt\u2014Reuss weight \\(\\alpha\\) . Example for glacier ice import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(4) # L=4 is sufficient here nlm = np.zeros((nlm_len), dtype=np.complex64) # array of expansion coefficients ### c-axis number distribution (nlm) from fourth-order structure tensor (a4) p = np.array([0,0,1]) # preferred c-axis direction a4 = np.einsum('i,j,k,l', p,p,p,p) # a4 if ODF = deltafunc(r-p) nlm[:sf.L4len] = sf.a4_to_nlm(a4) # determine l<=4 expansion coefficients of ODF ### Physical parameters (SI units) rho = 917 # density of ice Cij = (14.060e9, 15.240e9, 3.060e9, 7.150e9, 5.880e9) # Bennett (1968) parameters (C11,C33,C55,C12,C13) Lame_grain = sf.Cij_to_Lame_tranisotropic(Cij) # Lame parameters (lam,mu,Elam,Emu,Egam) alpha = 0.5 # Voigt--Reuss weight, where 0.5 = Hill average ### Propagation directions of interest theta, phi = np.deg2rad([90,70,]), np.deg2rad([0,10,]) # wave-vector directions (theta is colatitude, phi is longitude) ### Calculate phase velocities Vi = sf.Vi_elastic_tranisotropic(nlm, alpha, Lame_grain, rho, theta,phi) # phase velocities are V_S1=vi[0,:], V_S2=vi[1,:], V_P=vi[2,:] Evolving CPO The below animation shows directional P- and S-wave velocities for a CPO evolving under uniaxial compression along \\({\\hat {\\bf z}}\\) , relative to an isotropic CPO, when subject to lattice rotation . Orthotropic grains Monocrystal Polycrystal If grains are approximately orthotropic, the grain elastic behaviour can be modelled using the orthotropic elastic constitutive equation . This requires specifying the grain elastic parameters \\(\\lambda_{ij}'\\) , \\(\\mu_{i}'\\) , and the Voigt\u2014Reuss weight \\(\\alpha\\) . Example for olivine import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(4) # L=4 is sufficient here nlm = np.zeros((nlm_len), dtype=np.complex64) blm = np.zeros((nlm_len), dtype=np.complex64) ### Grain orientation distributions (nlm,blm) from fourth-order structure tensors vn = np.array([0,0,1]) # preferred slip-normal direction vb = np.array([1,0,0]) # preferred slip direction a4_n = np.einsum('i,j,k,l', vn,vn,vn,vn) # a4 if n/N ODF = deltafunc(r-vn) a4_b = np.einsum('i,j,k,l', vb,vb,vb,vb) # a4 if b/N ODF = deltafunc(r-vb) nlm[:sf.L4len] = sf.a4_to_nlm(a4_n) # determine l<=4 expansion coefficients of ODF blm[:sf.L4len] = sf.a4_to_nlm(a4_b) # determine l<=4 expansion coefficients of ODF ### Physical parameters (SI units) rho = 3355 # density of olivine Cij = (320.5e9, 196.5e9, 233.5e9, 64.0e9, 77.0e9, 78.7e9, 76.8e9, 71.6e9, 68.15e9) # Abramson (1997) parameters (C11,C22,C33,C44,C55,C66,C23,C13,C12) Lame_grain = sf.Cij_to_Lame_orthotropic(Cij) # Lame parameters (lam11,lam22,lam33, lam23,lam13,lam12, mu1,mu2,mu3) alpha = 1 # Voigt--Reuss weight; only alpha=1 supported for now ### Propagation directions of interest theta, phi = np.deg2rad([90,70,]), np.deg2rad([0,10,]) # wave-vector directions (theta is colatitude, phi is longitude) ### Calculate phase velocities vlm = 0*nlm # estimate vlm from (blm,nlm) by passing zero array Vi = sf.Vi_elastic_orthotropic(blm,nlm,vlm, alpha, Lame_grain, rho, theta,phi) # phase velocities are V_S1=vi[0,:], V_S2=vi[1,:], V_P=vi[2,:]","title":"Wave propagation"},{"location":"wavepropagation-elastic/#elastic-wave-propagation","text":"","title":"Elastic wave propagation"},{"location":"wavepropagation-elastic/#problem","text":"We seek plane wave solutions of the Cauchy-Navier equation of motion \\[ \\nabla\\cdot {\\boldsymbol \\sigma} = \\rho \\frac{\\partial^2 {\\bf d}}{\\partial t^2}, \\] where \\({\\boldsymbol \\sigma}\\) is the bulk stress tensor, \\({\\bf d}\\) is the displacement field, and \\(\\rho\\) the mass density. Substituting \\({\\bf d}\\) for a plane wave solution, \\({\\bf d} = {\\bf d}_0 \\exp[i({\\bf k}\\cdot {\\bf x} - \\omega t)]\\) , the problem reduces to \\[ (k^2\\hat{{\\bf Q}} - \\omega^2 \\rho {\\bf I}) {\\bf d} = {\\bf 0}, \\] where \\(\\hat{{\\bf Q}}(\\hat{{\\bf k}})\\) is the normalized acoustic tensor that varies depending on the bulk constitutive equation substituted for \\({\\boldsymbol \\sigma}({\\bf d})\\) . The above equation requires \\[ \\det( k^2\\hat{{\\bf Q}} - \\omega^2 \\rho {\\bf I} ) = 0 . \\] Evidently, the eigenvalues and eigenvectors of \\(\\hat{{\\bf Q}}/\\rho\\) are the permitted wave velocities squared and wave polarization, respectively, where \\[ V^2 = \\frac{\\omega^2}{k^2} \\] is the wave velocity squared. Homogenization The problem may be closed by approximating \\(\\hat{{\\bf Q}}\\) as the grain-averaged acoustic tensor, subject to a linear combination of the Voigt and Reuss homogenization schemes: \\[ \\hat{{\\bf Q}} = (1-\\alpha) \\langle\\hat{{\\bf Q}}'_{\\mathrm{Reuss}}\\rangle + \\alpha \\langle \\hat{{\\bf Q}}'_{\\mathrm{Voigt}}\\rangle . \\] The Voigt scheme ( \\(\\alpha=1\\) ) assumes the strain field is homogeneous over the polycrystal scale, whereas the Reuss scheme ( \\(\\alpha=0\\) ) assumes the stress is homogeneous. In these homogenizations, grains are therefore assumed interactionless and the bulk elastic behaviour is therefore simply the grain-orientation-averaged elastic behaviour subject to either homogeneous stress or strain assumptions over the polycrystal scale. Grain parameters The grain elastic parameters used for homogenization should be understood as the effective polycrystal values needed to reproduce experimental results; they are not the values derived from experiments on single crystals.","title":"Problem"},{"location":"wavepropagation-elastic/#transversely-isotropic-grains","text":"Monocrystal Polycrystal If grains are approximately transversely isotropic, the grain elastic behaviour can be modelled using the transversely isotropic elastic constitutive equation . This requires specifying the grain elastic parameters \\(\\lambda'\\) , \\(\\mu'\\) , \\(\\hat{\\lambda}'\\) , \\(\\hat{\\mu}'\\) , \\(\\hat{\\gamma}'\\) , and the Voigt\u2014Reuss weight \\(\\alpha\\) .","title":"Transversely isotropic grains"},{"location":"wavepropagation-elastic/#example-for-glacier-ice","text":"import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(4) # L=4 is sufficient here nlm = np.zeros((nlm_len), dtype=np.complex64) # array of expansion coefficients ### c-axis number distribution (nlm) from fourth-order structure tensor (a4) p = np.array([0,0,1]) # preferred c-axis direction a4 = np.einsum('i,j,k,l', p,p,p,p) # a4 if ODF = deltafunc(r-p) nlm[:sf.L4len] = sf.a4_to_nlm(a4) # determine l<=4 expansion coefficients of ODF ### Physical parameters (SI units) rho = 917 # density of ice Cij = (14.060e9, 15.240e9, 3.060e9, 7.150e9, 5.880e9) # Bennett (1968) parameters (C11,C33,C55,C12,C13) Lame_grain = sf.Cij_to_Lame_tranisotropic(Cij) # Lame parameters (lam,mu,Elam,Emu,Egam) alpha = 0.5 # Voigt--Reuss weight, where 0.5 = Hill average ### Propagation directions of interest theta, phi = np.deg2rad([90,70,]), np.deg2rad([0,10,]) # wave-vector directions (theta is colatitude, phi is longitude) ### Calculate phase velocities Vi = sf.Vi_elastic_tranisotropic(nlm, alpha, Lame_grain, rho, theta,phi) # phase velocities are V_S1=vi[0,:], V_S2=vi[1,:], V_P=vi[2,:] Evolving CPO The below animation shows directional P- and S-wave velocities for a CPO evolving under uniaxial compression along \\({\\hat {\\bf z}}\\) , relative to an isotropic CPO, when subject to lattice rotation .","title":"Example for glacier ice"},{"location":"wavepropagation-elastic/#orthotropic-grains","text":"Monocrystal Polycrystal If grains are approximately orthotropic, the grain elastic behaviour can be modelled using the orthotropic elastic constitutive equation . This requires specifying the grain elastic parameters \\(\\lambda_{ij}'\\) , \\(\\mu_{i}'\\) , and the Voigt\u2014Reuss weight \\(\\alpha\\) .","title":"Orthotropic grains"},{"location":"wavepropagation-elastic/#example-for-olivine","text":"import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(4) # L=4 is sufficient here nlm = np.zeros((nlm_len), dtype=np.complex64) blm = np.zeros((nlm_len), dtype=np.complex64) ### Grain orientation distributions (nlm,blm) from fourth-order structure tensors vn = np.array([0,0,1]) # preferred slip-normal direction vb = np.array([1,0,0]) # preferred slip direction a4_n = np.einsum('i,j,k,l', vn,vn,vn,vn) # a4 if n/N ODF = deltafunc(r-vn) a4_b = np.einsum('i,j,k,l', vb,vb,vb,vb) # a4 if b/N ODF = deltafunc(r-vb) nlm[:sf.L4len] = sf.a4_to_nlm(a4_n) # determine l<=4 expansion coefficients of ODF blm[:sf.L4len] = sf.a4_to_nlm(a4_b) # determine l<=4 expansion coefficients of ODF ### Physical parameters (SI units) rho = 3355 # density of olivine Cij = (320.5e9, 196.5e9, 233.5e9, 64.0e9, 77.0e9, 78.7e9, 76.8e9, 71.6e9, 68.15e9) # Abramson (1997) parameters (C11,C22,C33,C44,C55,C66,C23,C13,C12) Lame_grain = sf.Cij_to_Lame_orthotropic(Cij) # Lame parameters (lam11,lam22,lam33, lam23,lam13,lam12, mu1,mu2,mu3) alpha = 1 # Voigt--Reuss weight; only alpha=1 supported for now ### Propagation directions of interest theta, phi = np.deg2rad([90,70,]), np.deg2rad([0,10,]) # wave-vector directions (theta is colatitude, phi is longitude) ### Calculate phase velocities vlm = 0*nlm # estimate vlm from (blm,nlm) by passing zero array Vi = sf.Vi_elastic_orthotropic(blm,nlm,vlm, alpha, Lame_grain, rho, theta,phi) # phase velocities are V_S1=vi[0,:], V_S2=vi[1,:], V_P=vi[2,:]","title":"Example for olivine"},{"location":"wavepropagation-electromagnetic/","text":"Electromagnetic wave propagation Problem We seek plane wave solutions to Maxwell's equations in a non-conducting, source-free, anisotropic linear dielectric medium \\[ \\nabla^2 {\\bf E} = \\mu {\\boldsymbol \\epsilon} \\frac{\\partial^2 {\\bf E}}{\\partial t^2}, \\] where \\({\\bf E}\\) is the electric field, \\({\\boldsymbol \\epsilon}\\) is the bulk dielectric permittivity tensor, and \\(\\mu\\) the bulk isotropic permeability of the medium. Substituting \\({\\bf E}\\) for a plane wave solution, \\({\\bf E} = {\\bf E}_0 \\exp[i({\\bf k}\\cdot {\\bf x} - \\omega t)]\\) , the problem reduces to \\[ ({\\bf K} + \\omega^2 \\mu {\\boldsymbol \\epsilon}) {\\bf E} = {\\bf 0}, \\] where \\({\\bf K} = {\\bf k}\\times{\\bf k} \\times\\) is the matrix representation of the twice-applied cross product. The above equation requires \\[ \\det( {\\bf K} + \\omega^2 \\mu {\\boldsymbol \\epsilon} ) = 0 . \\] Evidently, the eigenvalues and eigenvectors of \\({\\boldsymbol \\epsilon}^{-1} {\\bf K}/(\\mu k^2)\\) are the permitted wave velocities squared and wave polarization, respectively, where \\[ V^2 = \\frac{\\omega^2}{k^2} \\] is the wave velocity squared. Homogenization If wave lengths are much longer than the average grain size, the problem can be closed by approximating the bulk polycrystalline permittivity tensor by the grain-averaged permittivity tensor \\[{\\boldsymbol \\epsilon} = \\langle {\\boldsymbol \\epsilon}' \\rangle,\\] constructed by averaging over all grain orientations (over the CPO). Transversely isotropic grains Monocrystal Polycrystal If grains are approximately transversely isotropic w.r.t. the symmetry axis \\({\\bf m}'\\) , the dielectric permittivity tensor of a single crystal can be written as $$ {\\boldsymbol \\epsilon}' = (2\\epsilon_{t}' + \\epsilon_{m}') \\frac{\\bf I}{3} + (\\epsilon_{m}'-\\epsilon_{t}') \\left( {\\bf m}'^2 - \\frac{\\bf I}{3} \\right), $$ where \\(\\epsilon_{m}'\\) and \\(\\epsilon_{t}'\\) are the permittivities parallel and perpendicular to the symmetry axis. In this case, the grain-averaged permitivity is simply \\[ {\\boldsymbol \\epsilon} = (2\\epsilon_{t}' + \\epsilon_{m}') \\frac{\\bf I}{3} + (\\epsilon_{m}'-\\epsilon_{t}') \\left( \\langle {\\bf m}'^2 \\rangle - \\frac{\\bf I}{3} \\right) , \\] where \\(\\langle {\\bf m}'^2 \\rangle\\) is the second-order structure tensor (aka \\({\\bf a}^{(2)}\\) ). Example for glacier ice To be documented. Orthotropic grains Not yet supported.","title":"Wave propagation"},{"location":"wavepropagation-electromagnetic/#electromagnetic-wave-propagation","text":"","title":"Electromagnetic wave propagation"},{"location":"wavepropagation-electromagnetic/#problem","text":"We seek plane wave solutions to Maxwell's equations in a non-conducting, source-free, anisotropic linear dielectric medium \\[ \\nabla^2 {\\bf E} = \\mu {\\boldsymbol \\epsilon} \\frac{\\partial^2 {\\bf E}}{\\partial t^2}, \\] where \\({\\bf E}\\) is the electric field, \\({\\boldsymbol \\epsilon}\\) is the bulk dielectric permittivity tensor, and \\(\\mu\\) the bulk isotropic permeability of the medium. Substituting \\({\\bf E}\\) for a plane wave solution, \\({\\bf E} = {\\bf E}_0 \\exp[i({\\bf k}\\cdot {\\bf x} - \\omega t)]\\) , the problem reduces to \\[ ({\\bf K} + \\omega^2 \\mu {\\boldsymbol \\epsilon}) {\\bf E} = {\\bf 0}, \\] where \\({\\bf K} = {\\bf k}\\times{\\bf k} \\times\\) is the matrix representation of the twice-applied cross product. The above equation requires \\[ \\det( {\\bf K} + \\omega^2 \\mu {\\boldsymbol \\epsilon} ) = 0 . \\] Evidently, the eigenvalues and eigenvectors of \\({\\boldsymbol \\epsilon}^{-1} {\\bf K}/(\\mu k^2)\\) are the permitted wave velocities squared and wave polarization, respectively, where \\[ V^2 = \\frac{\\omega^2}{k^2} \\] is the wave velocity squared. Homogenization If wave lengths are much longer than the average grain size, the problem can be closed by approximating the bulk polycrystalline permittivity tensor by the grain-averaged permittivity tensor \\[{\\boldsymbol \\epsilon} = \\langle {\\boldsymbol \\epsilon}' \\rangle,\\] constructed by averaging over all grain orientations (over the CPO).","title":"Problem"},{"location":"wavepropagation-electromagnetic/#transversely-isotropic-grains","text":"Monocrystal Polycrystal If grains are approximately transversely isotropic w.r.t. the symmetry axis \\({\\bf m}'\\) , the dielectric permittivity tensor of a single crystal can be written as $$ {\\boldsymbol \\epsilon}' = (2\\epsilon_{t}' + \\epsilon_{m}') \\frac{\\bf I}{3} + (\\epsilon_{m}'-\\epsilon_{t}') \\left( {\\bf m}'^2 - \\frac{\\bf I}{3} \\right), $$ where \\(\\epsilon_{m}'\\) and \\(\\epsilon_{t}'\\) are the permittivities parallel and perpendicular to the symmetry axis. In this case, the grain-averaged permitivity is simply \\[ {\\boldsymbol \\epsilon} = (2\\epsilon_{t}' + \\epsilon_{m}') \\frac{\\bf I}{3} + (\\epsilon_{m}'-\\epsilon_{t}') \\left( \\langle {\\bf m}'^2 \\rangle - \\frac{\\bf I}{3} \\right) , \\] where \\(\\langle {\\bf m}'^2 \\rangle\\) is the second-order structure tensor (aka \\({\\bf a}^{(2)}\\) ).","title":"Transversely isotropic grains"},{"location":"wavepropagation-electromagnetic/#example-for-glacier-ice","text":"To be documented.","title":"Example for glacier ice"},{"location":"wavepropagation-electromagnetic/#orthotropic-grains","text":"Not yet supported.","title":"Orthotropic grains"}]} \ No newline at end of file +{"config":{"indexing":"full","lang":["en"],"min_search_length":3,"prebuild_index":false,"separator":"[\\s\\-]+"},"docs":[{"location":"","text":"specfab documentation Spectral CPO model of polycrystalline materials that can: Model lattice rotation, discontinuous DRX, and rotation/continuous DRX. Calculate CPO-induced viscous anisotropies using Sachs/Taylor homogenizations. Calculate elastic P- and S-wave velocities using Voigt/Reuss homogenizations. Provide expressions for forward+inverse orthotropic and transversely isotropic rheologies. Convert between structure tensors and spectral expansions coefficients. Be integrated with finite-element codes such as Elmer and FEniCS. By Nicholas M. Rathmann and David A. Lilien Glacier ice demo Install Source code is available here . Environment How to install Python in Linux - PyPI package: pip3 install numpy --upgrade && pip3 install specfabpy - Compile yourself: cd src && make python Python in Windows/Mac You will have to compile specfab yourself. Fortran Run cd src && make specfab.o Elmer/Ice Interface Compile shared library by running cd src && make libspecfab.so Libraries required: BLAS, LAPACK Initialize Initialize specfab by running import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(10) # L=10 truncation is sufficient for many cases nlm = np.zeros(nlm_len, dtype=np.complex64) # vector of harmonic expansion coefficients nlm[0] = 1/np.sqrt(4*np.pi) # normalized isotropic distribution where Variable Interpretation nlm_len Number of expansion coefficients for expansion series truncated at \\(l=L\\) nlm Vector of complex-valued expansion coefficients (state vector) lm Vector of degree and order integers ( l , m ) associated with each entry in nlm","title":"About"},{"location":"#specfab-documentation","text":"Spectral CPO model of polycrystalline materials that can: Model lattice rotation, discontinuous DRX, and rotation/continuous DRX. Calculate CPO-induced viscous anisotropies using Sachs/Taylor homogenizations. Calculate elastic P- and S-wave velocities using Voigt/Reuss homogenizations. Provide expressions for forward+inverse orthotropic and transversely isotropic rheologies. Convert between structure tensors and spectral expansions coefficients. Be integrated with finite-element codes such as Elmer and FEniCS. By Nicholas M. Rathmann and David A. Lilien","title":"specfab documentation"},{"location":"#glacier-ice-demo","text":"","title":"Glacier ice demo"},{"location":"#install","text":"Source code is available here . Environment How to install Python in Linux - PyPI package: pip3 install numpy --upgrade && pip3 install specfabpy - Compile yourself: cd src && make python Python in Windows/Mac You will have to compile specfab yourself. Fortran Run cd src && make specfab.o Elmer/Ice Interface Compile shared library by running cd src && make libspecfab.so Libraries required: BLAS, LAPACK","title":"Install"},{"location":"#initialize","text":"Initialize specfab by running import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(10) # L=10 truncation is sufficient for many cases nlm = np.zeros(nlm_len, dtype=np.complex64) # vector of harmonic expansion coefficients nlm[0] = 1/np.sqrt(4*np.pi) # normalized isotropic distribution where Variable Interpretation nlm_len Number of expansion coefficients for expansion series truncated at \\(l=L\\) nlm Vector of complex-valued expansion coefficients (state vector) lm Vector of degree and order integers ( l , m ) associated with each entry in nlm","title":"Initialize"},{"location":"FEM-integration/","text":"Finite element integration The coupled problem The coupled evolution of flow and CPO can be solved by joining different components of specfab together with a Stokes flow solver (purple piece): That is, specfab can model/provides CPO evolution given the local large-scale stress, strain-rate and temperature (green piece) Viscous anisotropies induced by the local CPO (orange piece) Bulk anisotropic rheologies given the local viscous anisotropy (blue piece) Elmer/Ice See Lilien et al. (2023) for ice-flow modelling. FEniCS To be updated.","title":"FEM integration"},{"location":"FEM-integration/#finite-element-integration","text":"","title":"Finite element integration"},{"location":"FEM-integration/#the-coupled-problem","text":"The coupled evolution of flow and CPO can be solved by joining different components of specfab together with a Stokes flow solver (purple piece): That is, specfab can model/provides CPO evolution given the local large-scale stress, strain-rate and temperature (green piece) Viscous anisotropies induced by the local CPO (orange piece) Bulk anisotropic rheologies given the local viscous anisotropy (blue piece)","title":"The coupled problem"},{"location":"FEM-integration/#elmerice","text":"See Lilien et al. (2023) for ice-flow modelling.","title":"Elmer/Ice"},{"location":"FEM-integration/#fenics","text":"To be updated.","title":"FEniCS"},{"location":"constitutive-elastic/","text":"Elastic constitutive equations Linear elastic constituve equations are supported in both forward and inverse (or reverse) form. Transversely isotropic Symmetries Stiffness matrix \\({\\bf C}\\) for \\(\\bf{m}=\\hat{\\bf{z}}\\) \\(\\begin{bmatrix}\\gamma & \\lambda & \\hat{\\lambda}\\lambda & 0 & 0 & 0 \\\\\\lambda & \\gamma & \\hat{\\lambda}\\lambda & 0 & 0 & 0 \\\\\\hat{\\lambda}\\lambda & \\hat{\\lambda}\\lambda & \\hat{\\gamma}\\gamma & 0 & 0 & 0 \\\\0&0&0& \\hat{\\mu}\\mu & 0 & 0\\\\0&0&0& 0 & \\hat{\\mu}\\mu & 0\\\\0&0&0& 0 & 0 & \\mu\\\\\\end{bmatrix}\\) API E = sf.elas_fwd_tranisotropic(S, lam, mu, Elam, Emu, Egam, m) S = sf.elas_rev_tranisotropic(E, lam, mu, Elam, Emu, Egam, m) where Arguments Type S , E Stress and strain tensor (3x3) lam , mu Isotropic Lam\u00e9 parameters \\(\\lambda\\) and \\(\\mu\\) Elam , Emu , Egam Anisotropic enhancement factors \\(\\hat{\\lambda}\\) , \\(\\hat{\\mu}\\) , and \\(\\hat{\\gamma}\\) m Rotational symmetry axis \\(\\bf{m}\\) Note P-wave modulus is not a free parameter but given by \\(\\gamma \\equiv \\lambda + 2\\mu\\) . Tip: convert from \\(C_{ij}\\) to Lam\u00e9 parameters Cij = (C11,C33,C55, C12,C13) (lam,mu, Elam,Emu,Egam) = sf.Cij_to_Lame_tranisotropic(Cij) Orthotropic Symmetries Stiffness matrix \\({\\bf C}\\) for \\(({\\bf m}_1,{\\bf m}_2,{\\bf m}_3)=(\\hat{{\\bf x}},\\hat{{\\bf y}},\\hat{{\\bf z}})\\) \\(\\small\\begin{bmatrix} \\lambda_{11} + 2\\mu_1 & \\lambda_{12} & \\lambda_{13} & 0 & 0 & 0 \\\\ \\lambda_{12} & \\lambda_{22} + 2\\mu_2 & \\lambda_{23} & 0 & 0 & 0 \\\\ \\lambda_{13 }& \\lambda_{23} & \\lambda_{33} + 2\\mu_3 & 0 & 0 & 0 \\\\ 0 & 0 & 0 & \\dfrac{\\mu_2+\\mu_3}{2} & 0 & 0 \\\\ 0 & 0 & 0 & 0 & \\dfrac{\\mu_3+\\mu_1}{2} & 0 \\\\ 0 & 0 & 0 & 0 & 0 & \\dfrac{\\mu_1+\\mu_2}{2} \\end{bmatrix}\\) API Not yet made available E = sf.elas_fwd_orthotropic(S, lame, m1,m2,m3) S = sf.elas_rev_orthotropic(E, lame, m1,m2,m3) where Arguments Type S , E Stress and strain tensor (3x3) lame Tuple of anisotropic Lam\u00e9 parameters \\((\\lambda_{11},\\lambda_{22},\\lambda_{33}, \\lambda_{23},\\lambda_{13},\\lambda_{12}, \\mu_{1}, \\mu_{2}, \\mu_{3})\\) m1 , m2 , m3 Reflection symmetry axes \\(\\bf{m}_1\\) , \\(\\bf{m}_2\\) , and \\(\\bf{m}_3\\) Tip: convert from \\(C_{ij}\\) to Lam\u00e9 parameters Cij = (C11,C22,C33,C44,C55,C66, C23,C13,C12) (lam11,lam22,lam33, lam23,lam13,lam12, mu1,mu2,mu3) = sf.Cij_to_Lame_orthotropic(Cij)","title":"Constitutive equations"},{"location":"constitutive-elastic/#elastic-constitutive-equations","text":"Linear elastic constituve equations are supported in both forward and inverse (or reverse) form.","title":"Elastic constitutive equations"},{"location":"constitutive-elastic/#transversely-isotropic","text":"Symmetries Stiffness matrix \\({\\bf C}\\) for \\(\\bf{m}=\\hat{\\bf{z}}\\) \\(\\begin{bmatrix}\\gamma & \\lambda & \\hat{\\lambda}\\lambda & 0 & 0 & 0 \\\\\\lambda & \\gamma & \\hat{\\lambda}\\lambda & 0 & 0 & 0 \\\\\\hat{\\lambda}\\lambda & \\hat{\\lambda}\\lambda & \\hat{\\gamma}\\gamma & 0 & 0 & 0 \\\\0&0&0& \\hat{\\mu}\\mu & 0 & 0\\\\0&0&0& 0 & \\hat{\\mu}\\mu & 0\\\\0&0&0& 0 & 0 & \\mu\\\\\\end{bmatrix}\\)","title":"Transversely isotropic"},{"location":"constitutive-elastic/#api","text":"","title":"API"},{"location":"constitutive-elastic/#e-sfelas_fwd_tranisotropics-lam-mu-elam-emu-egam-m","text":"","title":"E = sf.elas_fwd_tranisotropic(S, lam, mu, Elam, Emu, Egam, m)"},{"location":"constitutive-elastic/#s-sfelas_rev_tranisotropice-lam-mu-elam-emu-egam-m","text":"where Arguments Type S , E Stress and strain tensor (3x3) lam , mu Isotropic Lam\u00e9 parameters \\(\\lambda\\) and \\(\\mu\\) Elam , Emu , Egam Anisotropic enhancement factors \\(\\hat{\\lambda}\\) , \\(\\hat{\\mu}\\) , and \\(\\hat{\\gamma}\\) m Rotational symmetry axis \\(\\bf{m}\\) Note P-wave modulus is not a free parameter but given by \\(\\gamma \\equiv \\lambda + 2\\mu\\) . Tip: convert from \\(C_{ij}\\) to Lam\u00e9 parameters Cij = (C11,C33,C55, C12,C13) (lam,mu, Elam,Emu,Egam) = sf.Cij_to_Lame_tranisotropic(Cij)","title":"S = sf.elas_rev_tranisotropic(E, lam, mu, Elam, Emu, Egam, m)"},{"location":"constitutive-elastic/#orthotropic","text":"Symmetries Stiffness matrix \\({\\bf C}\\) for \\(({\\bf m}_1,{\\bf m}_2,{\\bf m}_3)=(\\hat{{\\bf x}},\\hat{{\\bf y}},\\hat{{\\bf z}})\\) \\(\\small\\begin{bmatrix} \\lambda_{11} + 2\\mu_1 & \\lambda_{12} & \\lambda_{13} & 0 & 0 & 0 \\\\ \\lambda_{12} & \\lambda_{22} + 2\\mu_2 & \\lambda_{23} & 0 & 0 & 0 \\\\ \\lambda_{13 }& \\lambda_{23} & \\lambda_{33} + 2\\mu_3 & 0 & 0 & 0 \\\\ 0 & 0 & 0 & \\dfrac{\\mu_2+\\mu_3}{2} & 0 & 0 \\\\ 0 & 0 & 0 & 0 & \\dfrac{\\mu_3+\\mu_1}{2} & 0 \\\\ 0 & 0 & 0 & 0 & 0 & \\dfrac{\\mu_1+\\mu_2}{2} \\end{bmatrix}\\)","title":"Orthotropic"},{"location":"constitutive-elastic/#api_1","text":"Not yet made available","title":"API"},{"location":"constitutive-elastic/#e-sfelas_fwd_orthotropics-lame-m1m2m3","text":"","title":"E = sf.elas_fwd_orthotropic(S, lame, m1,m2,m3)"},{"location":"constitutive-elastic/#s-sfelas_rev_orthotropice-lame-m1m2m3","text":"where Arguments Type S , E Stress and strain tensor (3x3) lame Tuple of anisotropic Lam\u00e9 parameters \\((\\lambda_{11},\\lambda_{22},\\lambda_{33}, \\lambda_{23},\\lambda_{13},\\lambda_{12}, \\mu_{1}, \\mu_{2}, \\mu_{3})\\) m1 , m2 , m3 Reflection symmetry axes \\(\\bf{m}_1\\) , \\(\\bf{m}_2\\) , and \\(\\bf{m}_3\\) Tip: convert from \\(C_{ij}\\) to Lam\u00e9 parameters Cij = (C11,C22,C33,C44,C55,C66, C23,C13,C12) (lam11,lam22,lam33, lam23,lam13,lam12, mu1,mu2,mu3) = sf.Cij_to_Lame_orthotropic(Cij)","title":"S = sf.elas_rev_orthotropic(E, lame, m1,m2,m3)"},{"location":"constitutive-viscoplastic/","text":"Viscoplastic constitutive equations Anisotropic power-law rheologies are supported in both forward and inverse (or reverse) form. Eigenenhancements Bulk viscous anisotropy is prescribed in terms of logitudinal and shear strain-rate enhancement factors w.r.t rheological symmetry axes, termed eigenenhancements ( \\(E_{ij}\\) ). Transversely isotropic Rheolgical symmetries Forward rheology $$ {\\bf D} = \\eta^{-1} \\Big( {\\bf S} - \\lambda_1 ({\\bf S}:{\\bf M}){\\bf I} + \\lambda_2 ({\\bf S}:{\\bf M}){\\bf M} + \\lambda_3 ({\\bf S}\\cdot{\\bf M} + {\\bf M}\\cdot{\\bf S}) \\Big) $$ $$ \\eta^{-1} = A\\Big( {\\bf S}:{\\bf S} + \\lambda_2 ({\\bf S}:{\\bf M})^2 + 2\\lambda_2 ({\\bf S}^2:{\\bf M}) \\Big)^{(n-1)/2} $$ $$ {\\bf M}={\\bf m}^2$$ where the material parameters \\(\\lambda_i\\) depend on the eigenenhancements: \\[ \\lambda_1 = \\frac{E_{mm}^{2/(n+1)}-1}{2} ,\\quad \\lambda_2 = \\frac{3(E_{mm}^{2/(n+1)}-1) - 4(E_{mt}^{2/(n+1)}-1)}{2} ,\\quad \\lambda_3 = E_{mt}^{2/(n+1)}-1 \\] API D = sf.rheo_fwd_tranisotropic(S, A, n, m, Eij) S = sf.rheo_rev_tranisotropic(D, A, n, m, Eij) where Arguments Type S , D Deviatoric-stress and strain-rate tensor (3x3) A , n Flow-rate factor \\(A\\) and power-law exponent \\(n\\) m Rotational symmetry axis \\(\\bf{m}\\) Eij Tuple of eigenenhancements (Emm, Emt) Orthotropic Rheolgical symmetries Forward rheology \\({\\bf D} = \\eta^{-1} \\displaystyle\\sum_{i=1}^{3} \\Big[ \\lambda_i ({\\bf S}:{\\bf M}_i){\\bf M}_{i} + \\lambda_{i+3} ({\\bf S}:{\\bf M}_{i+3}) {\\bf M}_{i+3} \\Big]\\) \\(\\eta^{-1} = A\\left( \\displaystyle\\sum_{i=1}^3 \\Big[ \\lambda_i ({\\bf S}:{\\bf M}_{i})^2 + \\lambda_{i+3} ({\\bf S}:{\\bf M}_{i+3})^2 \\Big] \\right)^{(n-1)/2}\\) \\({\\bf M}_{i} = \\dfrac{{\\bf m}_{j_i} {\\bf m}_{j_i} - {\\bf m}_{k_i} {\\bf m}_{k_i}}{2} ,\\quad {\\bf M}_{i+3} = \\dfrac{{\\bf m}_{j_i} {\\bf m}_{k_i} + {\\bf m}_{k_i} {\\bf m}_{j_i}}{2},\\) \\((j_1, j_2, j_3) = (2,3,1),\\quad (k_1, k_2, k_3) = (3,1,2)\\) where the material parameters \\(\\lambda_i\\) depend on the eigenenhancements: \\[ \\lambda_i = \\frac{4}{3} \\left( E_{j_i j_i}^{2/(n+1)} + E_{k_i k_i}^{2/(n+1)} - E_{i i}^{2/(n+1)} \\right),\\quad \\lambda_{i+3} = 2 E_{j_i k_i}^{2/(n+1)} \\] API D = sf.rheo_fwd_orthotropic(S, A, n, m1,m2,m3, Eij) S = sf.rheo_rev_orthotropic(D, A, n, m1,m2,m3, Eij) where Arguments Type S , D Deviatoric-stress and strain-rate tensor (3x3) A , n Flow-rate factor \\(A\\) and power-law exponent \\(n\\) m1 , m2 , m3 Reflection symmetry axes \\(\\bf{m}_1\\) , \\(\\bf{m}_2\\) , and \\(\\bf{m}_3\\) Eij Tuple of eigenenhancements (E11,E22,E33,E23,E13,E12)","title":"Constitutive equations"},{"location":"constitutive-viscoplastic/#viscoplastic-constitutive-equations","text":"Anisotropic power-law rheologies are supported in both forward and inverse (or reverse) form. Eigenenhancements Bulk viscous anisotropy is prescribed in terms of logitudinal and shear strain-rate enhancement factors w.r.t rheological symmetry axes, termed eigenenhancements ( \\(E_{ij}\\) ).","title":"Viscoplastic constitutive equations"},{"location":"constitutive-viscoplastic/#transversely-isotropic","text":"Rheolgical symmetries Forward rheology $$ {\\bf D} = \\eta^{-1} \\Big( {\\bf S} - \\lambda_1 ({\\bf S}:{\\bf M}){\\bf I} + \\lambda_2 ({\\bf S}:{\\bf M}){\\bf M} + \\lambda_3 ({\\bf S}\\cdot{\\bf M} + {\\bf M}\\cdot{\\bf S}) \\Big) $$ $$ \\eta^{-1} = A\\Big( {\\bf S}:{\\bf S} + \\lambda_2 ({\\bf S}:{\\bf M})^2 + 2\\lambda_2 ({\\bf S}^2:{\\bf M}) \\Big)^{(n-1)/2} $$ $$ {\\bf M}={\\bf m}^2$$ where the material parameters \\(\\lambda_i\\) depend on the eigenenhancements: \\[ \\lambda_1 = \\frac{E_{mm}^{2/(n+1)}-1}{2} ,\\quad \\lambda_2 = \\frac{3(E_{mm}^{2/(n+1)}-1) - 4(E_{mt}^{2/(n+1)}-1)}{2} ,\\quad \\lambda_3 = E_{mt}^{2/(n+1)}-1 \\]","title":"Transversely isotropic"},{"location":"constitutive-viscoplastic/#api","text":"","title":"API"},{"location":"constitutive-viscoplastic/#d-sfrheo_fwd_tranisotropics-a-n-m-eij","text":"","title":"D = sf.rheo_fwd_tranisotropic(S, A, n, m, Eij)"},{"location":"constitutive-viscoplastic/#s-sfrheo_rev_tranisotropicd-a-n-m-eij","text":"where Arguments Type S , D Deviatoric-stress and strain-rate tensor (3x3) A , n Flow-rate factor \\(A\\) and power-law exponent \\(n\\) m Rotational symmetry axis \\(\\bf{m}\\) Eij Tuple of eigenenhancements (Emm, Emt)","title":"S = sf.rheo_rev_tranisotropic(D, A, n, m, Eij)"},{"location":"constitutive-viscoplastic/#orthotropic","text":"Rheolgical symmetries Forward rheology \\({\\bf D} = \\eta^{-1} \\displaystyle\\sum_{i=1}^{3} \\Big[ \\lambda_i ({\\bf S}:{\\bf M}_i){\\bf M}_{i} + \\lambda_{i+3} ({\\bf S}:{\\bf M}_{i+3}) {\\bf M}_{i+3} \\Big]\\) \\(\\eta^{-1} = A\\left( \\displaystyle\\sum_{i=1}^3 \\Big[ \\lambda_i ({\\bf S}:{\\bf M}_{i})^2 + \\lambda_{i+3} ({\\bf S}:{\\bf M}_{i+3})^2 \\Big] \\right)^{(n-1)/2}\\) \\({\\bf M}_{i} = \\dfrac{{\\bf m}_{j_i} {\\bf m}_{j_i} - {\\bf m}_{k_i} {\\bf m}_{k_i}}{2} ,\\quad {\\bf M}_{i+3} = \\dfrac{{\\bf m}_{j_i} {\\bf m}_{k_i} + {\\bf m}_{k_i} {\\bf m}_{j_i}}{2},\\) \\((j_1, j_2, j_3) = (2,3,1),\\quad (k_1, k_2, k_3) = (3,1,2)\\) where the material parameters \\(\\lambda_i\\) depend on the eigenenhancements: \\[ \\lambda_i = \\frac{4}{3} \\left( E_{j_i j_i}^{2/(n+1)} + E_{k_i k_i}^{2/(n+1)} - E_{i i}^{2/(n+1)} \\right),\\quad \\lambda_{i+3} = 2 E_{j_i k_i}^{2/(n+1)} \\]","title":"Orthotropic"},{"location":"constitutive-viscoplastic/#api_1","text":"","title":"API"},{"location":"constitutive-viscoplastic/#d-sfrheo_fwd_orthotropics-a-n-m1m2m3-eij","text":"","title":"D = sf.rheo_fwd_orthotropic(S, A, n, m1,m2,m3, Eij)"},{"location":"constitutive-viscoplastic/#s-sfrheo_rev_orthotropicd-a-n-m1m2m3-eij","text":"where Arguments Type S , D Deviatoric-stress and strain-rate tensor (3x3) A , n Flow-rate factor \\(A\\) and power-law exponent \\(n\\) m1 , m2 , m3 Reflection symmetry axes \\(\\bf{m}_1\\) , \\(\\bf{m}_2\\) , and \\(\\bf{m}_3\\) Eij Tuple of eigenenhancements (E11,E22,E33,E23,E13,E12)","title":"S = sf.rheo_rev_orthotropic(D, A, n, m1,m2,m3, Eij)"},{"location":"cpo-dynamics-orthotropic/","text":"CPO dynamics for orthotropic grains This tutorial focuses on modelling the CPO evolution of polycrystalline olivine. That is, the distribution of (easy) slip-plane normals and slip directions of grains, \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) . Polycrystalline olivine Ensemble of slip elements The distributions \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) refer to certain crystallographic axes ( \\({\\bf m}'_i\\) ) depending on the fabric type; i.e. thermodynamic conditions, water content, and stress magnitude that control which of the crystallographic slip systems is activated. Problem Given the expansions \\[ n({\\bf x},t,\\theta,\\phi)=\\sum_{l=0}^{L}\\sum_{m=-l}^{l}n_{l}^{m}({\\bf x},t) Y_{l}^{m}(\\theta,\\phi) \\quad\\text{(distribution of slip-plane normals)}, \\\\ b({\\bf x},t,\\theta,\\phi)=\\sum_{l=0}^{L}\\sum_{m=-l}^{l}b_{l}^{m}({\\bf x},t) Y_{l}^{m}(\\theta,\\phi) \\quad\\text{(distribution of slip directions)}, \\] CPO evolution can be written as two independent matrix problems involving the CPO state vector fields \\[ {\\bf s}_n = [n_0^0,n_2^{-2},n_2^{-1},n_2^{0},n_2^{1},n_2^{2},n_4^{-4},\\cdots,n_4^{4},\\cdots,n_L^{-L},\\cdots,n_L^{L}]^{\\mathrm{T}} \\quad\\text{($n$ state vector)}, \\\\ {\\bf s}_b = [b_0^0,b_2^{-2},b_2^{-1},b_2^{0},b_2^{1},b_2^{2},b_4^{-4},\\cdots,b_4^{4},\\cdots,b_L^{-L},\\cdots,b_L^{L}]^{\\mathrm{T}} \\quad\\text{($b$ state vector)}, \\] such that \\[ \\frac{\\mathrm{D}{\\bf s}_n}{\\mathrm{D} t} = {\\bf M}_n \\cdot {\\bf s}_n \\quad\\text{($n$ state evolution)}, \\\\ \\frac{\\mathrm{D}{\\bf s}_b}{\\mathrm{D} t} = {\\bf M}_b \\cdot {\\bf s}_b \\quad\\text{($b$ state evolution)}, \\] where the operators (matrices) \\({\\bf M}_n\\) and \\({\\bf M}_b\\) represents the effect of a given CPO process, which may depend on stress, strain-rate, temperature, etc. Note The distributions may also be understood as the mass density fraction of grains with a given slip-plane-normal and slip-direction orientation. See CPO representation for details. Lagrangian parcel The tutorial shows how to model the CPO evolution of a Lagrangian material parcel subject to three different modes of deformation : Lattice rotation To be published before documented here. Regularization Same as CPO dynamics for transversely isotropic grains","title":"Orthotropic grains"},{"location":"cpo-dynamics-orthotropic/#cpo-dynamics-for-orthotropic-grains","text":"This tutorial focuses on modelling the CPO evolution of polycrystalline olivine. That is, the distribution of (easy) slip-plane normals and slip directions of grains, \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) . Polycrystalline olivine Ensemble of slip elements The distributions \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) refer to certain crystallographic axes ( \\({\\bf m}'_i\\) ) depending on the fabric type; i.e. thermodynamic conditions, water content, and stress magnitude that control which of the crystallographic slip systems is activated.","title":"CPO dynamics for orthotropic grains"},{"location":"cpo-dynamics-orthotropic/#problem","text":"Given the expansions \\[ n({\\bf x},t,\\theta,\\phi)=\\sum_{l=0}^{L}\\sum_{m=-l}^{l}n_{l}^{m}({\\bf x},t) Y_{l}^{m}(\\theta,\\phi) \\quad\\text{(distribution of slip-plane normals)}, \\\\ b({\\bf x},t,\\theta,\\phi)=\\sum_{l=0}^{L}\\sum_{m=-l}^{l}b_{l}^{m}({\\bf x},t) Y_{l}^{m}(\\theta,\\phi) \\quad\\text{(distribution of slip directions)}, \\] CPO evolution can be written as two independent matrix problems involving the CPO state vector fields \\[ {\\bf s}_n = [n_0^0,n_2^{-2},n_2^{-1},n_2^{0},n_2^{1},n_2^{2},n_4^{-4},\\cdots,n_4^{4},\\cdots,n_L^{-L},\\cdots,n_L^{L}]^{\\mathrm{T}} \\quad\\text{($n$ state vector)}, \\\\ {\\bf s}_b = [b_0^0,b_2^{-2},b_2^{-1},b_2^{0},b_2^{1},b_2^{2},b_4^{-4},\\cdots,b_4^{4},\\cdots,b_L^{-L},\\cdots,b_L^{L}]^{\\mathrm{T}} \\quad\\text{($b$ state vector)}, \\] such that \\[ \\frac{\\mathrm{D}{\\bf s}_n}{\\mathrm{D} t} = {\\bf M}_n \\cdot {\\bf s}_n \\quad\\text{($n$ state evolution)}, \\\\ \\frac{\\mathrm{D}{\\bf s}_b}{\\mathrm{D} t} = {\\bf M}_b \\cdot {\\bf s}_b \\quad\\text{($b$ state evolution)}, \\] where the operators (matrices) \\({\\bf M}_n\\) and \\({\\bf M}_b\\) represents the effect of a given CPO process, which may depend on stress, strain-rate, temperature, etc. Note The distributions may also be understood as the mass density fraction of grains with a given slip-plane-normal and slip-direction orientation. See CPO representation for details.","title":"Problem"},{"location":"cpo-dynamics-orthotropic/#lagrangian-parcel","text":"The tutorial shows how to model the CPO evolution of a Lagrangian material parcel subject to three different modes of deformation :","title":"Lagrangian parcel"},{"location":"cpo-dynamics-orthotropic/#lattice-rotation","text":"To be published before documented here.","title":"Lattice rotation"},{"location":"cpo-dynamics-orthotropic/#regularization","text":"Same as CPO dynamics for transversely isotropic grains","title":"Regularization"},{"location":"cpo-dynamics-tranisotropic/","text":"CPO dynamics for transversely isotropic grains This tutorial focuses on modelling the CPO evolution of polycrystalline glacier ice. That is, the distribution \\(n(\\theta,\\phi)\\) of (easy) slip-plane normals of grains ( \\({\\bf n}={\\bf c}\\) ). Polycrystalline ice Ensemble of slip elements Problem Given the expansion \\[ n({\\bf x},t,\\theta,\\phi)=\\sum_{l=0}^{L}\\sum_{m=-l}^{l}n_{l}^{m}({\\bf x},t) Y_{l}^{m}(\\theta,\\phi) \\quad\\text{(distribution of slip-plane normals)}, \\] CPO evolution can be written as a matrix problem involving the state vector \\[ {\\bf s} = [n_0^0,n_2^{-2},n_2^{-1},n_2^{0},n_2^{1},n_2^{2},n_4^{-4},\\cdots,n_4^{4},\\cdots,n_L^{-L},\\cdots,n_L^{L}]^{\\mathrm{T}} \\quad\\text{(state vector)}, \\] such that \\[ \\frac{\\mathrm{D}{\\bf s}}{\\mathrm{D} t} = {\\bf M} \\cdot {\\bf s} \\quad\\text{(state evolution)}, \\] where the operator (matrix) \\({\\bf M}\\) represents the effect of a given CPO process, which may depend on stress, strain-rate, temperature, etc. The total effect of multiple processes acting simultaneously is simply \\[ {\\bf M} = {\\bf M_{\\mathrm{LROT}}} + {\\bf M_{\\mathrm{DDRX}}} + {\\bf M_{\\mathrm{CDRX}}} + \\cdots \\quad\\text{(operator)}. \\] Note \\(n(\\theta,\\phi)\\) may also be understood as the mass density fraction of grains with a given slip-plane normal orientation. See CPO representation for details. Lagrangian parcel The tutorial shows how to model the CPO evolution of a Lagrangian material parcel subject to three different modes of deformation : Lattice rotation The strain-induced rotation of \\(c\\) -axes is modelled given the (local) velocity gradient tensor \\(\\nabla {\\bf u}\\) ( Svendsen and Hutter, 1996 ). The model is a kinematic model in the sense that \\(c\\) -axes rotate in response to the bulk rate of stretching, \\({\\bf D}\\) , and spin, \\({\\bf W}\\) , thereby allowing the detailed microscopic stress and strain rate fields to be neglected and hence interactions between neighboring grains to be disregarded. The modelled \\(c\\) -axes are taken to rotate with the bulk continuum spin ( \\({\\bf W}\\) ), plus some plastic spin correction ( \\({\\bf W}_{\\mathrm{p}}\\) ), so that the \\(c\\) -axis velocity field on the unit sphere is \\[ {\\bf \\dot{c}} = ({\\bf W} + {\\bf W}_{\\mathrm{p}}) \\cdot {\\hat {\\bf r}} \\quad\\text{($c$-axis velocity field on $S^2$)} , \\] where \\({\\hat {\\bf r}}\\) is the radial unit vector, and the plastic spin depends on \\({\\bf D}\\) to lowest and second lowest order following Wang (1969) and Aravas (1994) : \\[ {\\bf W}_{\\mathrm{p}} = \\iota({\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}\\cdot{\\bf D} - {\\bf D}\\cdot{\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}) + \\zeta({\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}\\cdot{\\bf D}^2 - {\\bf D}^2\\cdot{\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}) . \\] By requiring that basal planes preserve their orientation when subject to simple shear (like a deck of cards), it can be shown that \\(\\iota=1\\) and \\(\\zeta=0\\) . The corresponding effect on the continuous \\(c\\) -axis distribution is modelled as a conservative advection process on the surface of the unit sphere: \\[ \\frac{\\mathrm{D} n}{\\mathrm{D} t} = -\\nabla_{S^2}\\cdot(n{\\bf \\dot{c}}) \\quad\\Longrightarrow\\quad \\frac{\\mathrm{D} {\\bf s}}{\\mathrm{D} t} = {\\bf M_{\\mathrm{LROT}}} \\cdot {\\bf s}, \\] where \\({\\bf M_{\\mathrm{LROT}}}\\) is given analytically in Rathmann et al. (2021) . c-axis velocity field The normalized \\(c\\) -axis velocity fields for the three modes of deformation considered are: Example import numpy as np from specfabpy import specfab as sf # L=8 truncation is sufficient in this case, but larger L allows a very strong fabric to develop # and minimizes the effect that regularization has on low wavenumber modes (l=2,4) lm, nlm_len = sf.init(8) ### Velocity gradient tensor experienced by parcel ugrad = np.diag([0.5, 0.5, -1.0]) # Unconfined compression along z-direction (equal extension in x and y) D = (ugrad+np.transpose(ugrad))/2 # Symmetric part (strain-rate tensor) W = (ugrad-np.transpose(ugrad))/2 # Anti-symmetric part (spin tensor) ### Numerics Nt = 25 # Number of time steps dt = 0.05 # Time-step size ### Initialize fabric as isotropic nlm = np.zeros((Nt,nlm_len), dtype=np.complex64) # State vector (array of expansion coefficients) nlm[0,0] = 1/np.sqrt(4*np.pi) # Normalized ODF at t=0 ### Euler integration of lattice rotation + regularization for tt in np.arange(1,Nt): nlm_prev = nlm[tt-1,:] # Previous solution iota, zeta = 1, 0 # \"Deck of cards\" behavior M_LROT = sf.M_LROT(nlm_prev, D, W, iota, zeta) # Lattice rotation operator (nlm_len x nlm_len matrix) M_REG = sf.M_REG(nlm_prev, D) # Regularization operator (nlm_len x nlm_len matrix) M = M_LROT + M_REG nlm[tt,:] = nlm_prev + dt*np.matmul(M, nlm_prev) # Euler step nlm[tt,:] = sf.apply_bounds(nlm[tt,:]) # Apply spectral bounds if needed # See page \"Miscellaneous --> Plotting\" for how to plot resulting ODFs given nlm Discontinous dynamic recrystallization (DDRX) DDRX is modelled as a grain orientation or mass decay/production process on the unit sphere ( Placidi and others, 2010 ): \\[ \\frac{\\mathrm{D} n}{\\mathrm{D} t} = \\Gamma n \\quad\\Longrightarrow\\quad \\frac{\\mathrm{D} {\\bf s}}{\\mathrm{D} t} = {\\bf M_{\\mathrm{DDRX}}} \\cdot {\\bf s} , \\] where the decay/production rate \\[\\Gamma = \\Gamma_0\\left(D- {\\langle} D {\\rangle}\\right) \\quad\\text{(decay/production rate)}\\] depends on the rate magnitude \\(\\Gamma_0\\) , and the deformability \\(D\\) as a function of the stress tensor \\({\\bf S}\\) : \\[ D = \\frac{({\\bf S}\\cdot{\\bf S}):({\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}) - {\\bf S}:({\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}):{\\bf S}}{{\\bf S}:{\\bf S}}\\quad\\text{(deformability)} . \\] \\({\\bf M_{\\mathrm{DDRX}}}\\) is given analytically in Rathmann and Lilien (2021) . Note The average deformability, \\(\\langle D\\rangle\\) , depends on the instantaneous CPO state \u2014 specifically, the structure tensors a2 and a4 \u2014 making the corresponding matrix problem nonlinear by conserving the total number of grain orientations or mass density depending on how normalization is interpreted , the latter arguably resting on stronger physical grounds. Decay/production rate The normalized DDRX decay/production rate \\(\\Gamma/\\Gamma_0 = D - \\langle D \\rangle\\) is an orientation dependent field that favors nucleation (orientation/mass production) in the directions where the resolved basal-plane shear stress is maximal, and orientation/mass decay elsewhere. The the normalized decay rate for the three modes of deformation considered are: Example import numpy as np from specfabpy import specfab as sf # L=8 truncation is sufficient in this case, but larger L allows a very strong fabric to develop # and minimizes the effect that regularization has on low wavenumber modes (l=2,4) lm, nlm_len = sf.init(8) ### Stress tensor experienced by parcel S = np.diag([0.0, 1.0, -1.0]) # Confined compression along z-direction (extension confined to y-direction) #S = np.diag([0.5, 0.5, -1.0]) # Unconfined compression along z-direction Gamma0 = 10 # DDRX decay-rate magnitude (may depend on temperature, strain-rate, and other factors, see e.g. Richards et al., 2021) ### Numerics Nt = 25 # Number of time steps dt = 0.05 # Time-step size ### Initialize fabric as isotropic nlm = np.zeros((Nt,nlm_len), dtype=np.complex64) # State vector (array of expansion coefficients) nlm[0,0] = 1/np.sqrt(4*np.pi) # Normalized ODF at t=0 ### Euler integration of DDRX for tt in np.arange(1,Nt): nlm_prev = nlm[tt-1,:] # Previous solution M = Gamma0 * sf.M_DDRX(nlm_prev, S) # DDRX operator (nlm_len x nlm_len matrix) nlm[tt,:] = nlm_prev + dt*np.matmul(M, nlm_prev) # Complete Euler step nlm[tt,:] = sf.apply_bounds(nlm[tt,:]) # Apply spectral bounds if needed # See page \"Miscellaneous --> Plotting\" for how to plot resulting ODFs given nlm Continous dynamic recrystallization (CDRX) Polygonization (rotation recrystallization, CDRX) accounts for the division of grains along internal sub-grain boundaries when exposed to bending stresses. In effect, CDRX reduces the average grain size upon grain division but does not necessarily change the CPO much ( Alley, 1992 ). The model follows G\u00f6dert (2003) by approximating the effect of CDRX on the distribution of grain orientations/mass as a Laplacian diffusive process on \\(S^2\\) : \\[ \\frac{\\mathrm{D} n}{\\mathrm{D} t} = \\Lambda\\nabla^2 n \\quad\\Longrightarrow\\quad \\frac{\\mathrm{D} {\\bf s}}{\\mathrm{D} t} = {\\bf M_{\\mathrm{CDRX}}} \\cdot {\\bf s} . \\] Example To model CDRX, add the following contribution to the total fabric operator \\({\\bf M}\\) : M += Lambda*sf.M_CDRX(nlm) where Lambda is the CDRX rate-factor magnitude, \\(\\Lambda\\) , that possibly depends on temperature, stress, strain-rate, etc. ( Richards et al., 2021 ). Regularization As \\(n(\\theta,\\phi)\\) becomes anisotropic due to CPO processes, the coefficients \\(n_l^m\\) associated with high wavenumber modes (large \\(l\\) and \\(m\\) , and thus small-scale structure) must increase in magnitude relative to the low wavenumber coefficients (small \\(l\\) and \\(m\\) ). One way to visualize this is by the angular power spectrum \\[ S(l) = \\frac{1}{2l + 1} \\sum_{m=-l}^l \\left\\vert n_l^m \\right\\vert^2 , \\] which grows with time. In the animation above, the left-hand panel shows how the power spectrum evolves under lattice rotation (unconfined vertical compression) compared to the end-member case of a delta function (dashed line). If the expansion series is truncated at \\(l=L\\) , then \\(l{\\gt}L\\) modes cannot evolve, and the truncated solution will reach an unphysical quasi-steady state. To prevent this, regularization must be introduced. Specfab uses Laplacian hyper diffusion ( \\(k>1\\) ) as regularization in \\(S^2\\) \\[ \\frac{\\mathrm{D} n_l^m}{\\mathrm{D} t} ={\\nu}[l(l+1)]^{k} n_l^m \\quad\\Longrightarrow\\quad \\frac{\\mathrm{D} {\\bf s}}{\\mathrm{D} t} = {\\bf M_{\\mathrm{REG}}} \\cdot {\\bf s} , \\] that can be added to the fabric evolution operator \\({\\bf M}\\) as follows: M += sf.M_REG(nlm, D) This allows the growth of high wavenumber modes to be disproportionately damped (green line compared to red line in animation above). Note As a rule-of-thumb, regularization affects the highest and next-highest modes \\(l{\\geq}L-2\\) and can therefore not be expected to evolve freely. This, in turn, means that structure tensors a2 and a4 , and hence calculated enhancement factors , might be affected by regularization unless \\(L{\\geq}8\\) is chosen. High-level integrator Alternatively, you can use the high-level Lagrangian parcel integrator for constant stress/strain-rate: import numpy as np from specfabpy import specfab as sf from specfabpy import integrator as sfint lm, nlm_len = sf.init(8) ### Process rate factors etc. iota, zeta = 1, 0 # deck-of-cards behaviour for lattice rotation (=None => disabled) nu = 1 # scale the regularization magnitude by this amount (=None => no regularization) Gamma0 = Lambda = None # DDRX and CDRX rate factors (=None => disabled) ### Mode of deformation (mod) and parcel strain target # Note: axes 0,1,2 = x,y,z, planes 0,1,2 = yz,xz,xy # See page \"Miscellaneous --> Deformation Modes\" for T and r definitions mod, target = dict(type='ps', axis=2, T=+1, r=0), -0.95 # uniaxial compression along z until strain_zz = target mod, target = dict(type='ps', axis=2, T=-1, r=0), 6 # uniaxial extension along z until strain_zz = target mod, target = dict(type='ss', plane=1, T=+1), np.deg2rad(80) # simple shear until arctan(strain_xz) = target ### Integrate parcel CPO evolution nlm0 = np.zeros((nlm_len), dtype=np.complex64) nlm0[0] = 1/np.sqrt(4*np.pi) # normalized and isotropic initial state Nt = 200 # Number of integration steps nlm, F, time, ugrad = sfint.lagrangianparcel(sf, mod, target, Nt=Nt, nlm0=nlm0, iota=iota, zeta=zeta, Gamma0=Gamma0, Lambda=Lambda, nu=nu) # returns (state vector, deformation gradient tensor, time vector, velocity gradient) # See page \"Miscellaneous --> Plotting\" for how to plot resulting ODFs given nlm and parcel shape given F Validation If the CPO is rotated into an approximately rotationally-symmetric frame about the \\(z\\) -axis, then only \\(n_l^0\\) components are nonzero. This conveniently allows validating modelled CPO processes by comparing modelled to observed correlations between, e.g., the lowest-order normalized components \\(\\hat{n}_2^0 = n_2^0/n_0^0\\) and \\(\\hat{n}_4^0 = n_4^0/n_0^0\\) . The below plot from Lilien et al. (2023) shows the observed correlation structure (markers) compared to the above CPO model(s) for different modes of deformation, suggesting that modelled CPO processes capture observations reasonably well.","title":"Transversely isotropic grains"},{"location":"cpo-dynamics-tranisotropic/#cpo-dynamics-for-transversely-isotropic-grains","text":"This tutorial focuses on modelling the CPO evolution of polycrystalline glacier ice. That is, the distribution \\(n(\\theta,\\phi)\\) of (easy) slip-plane normals of grains ( \\({\\bf n}={\\bf c}\\) ). Polycrystalline ice Ensemble of slip elements","title":"CPO dynamics for transversely isotropic grains"},{"location":"cpo-dynamics-tranisotropic/#problem","text":"Given the expansion \\[ n({\\bf x},t,\\theta,\\phi)=\\sum_{l=0}^{L}\\sum_{m=-l}^{l}n_{l}^{m}({\\bf x},t) Y_{l}^{m}(\\theta,\\phi) \\quad\\text{(distribution of slip-plane normals)}, \\] CPO evolution can be written as a matrix problem involving the state vector \\[ {\\bf s} = [n_0^0,n_2^{-2},n_2^{-1},n_2^{0},n_2^{1},n_2^{2},n_4^{-4},\\cdots,n_4^{4},\\cdots,n_L^{-L},\\cdots,n_L^{L}]^{\\mathrm{T}} \\quad\\text{(state vector)}, \\] such that \\[ \\frac{\\mathrm{D}{\\bf s}}{\\mathrm{D} t} = {\\bf M} \\cdot {\\bf s} \\quad\\text{(state evolution)}, \\] where the operator (matrix) \\({\\bf M}\\) represents the effect of a given CPO process, which may depend on stress, strain-rate, temperature, etc. The total effect of multiple processes acting simultaneously is simply \\[ {\\bf M} = {\\bf M_{\\mathrm{LROT}}} + {\\bf M_{\\mathrm{DDRX}}} + {\\bf M_{\\mathrm{CDRX}}} + \\cdots \\quad\\text{(operator)}. \\] Note \\(n(\\theta,\\phi)\\) may also be understood as the mass density fraction of grains with a given slip-plane normal orientation. See CPO representation for details.","title":"Problem"},{"location":"cpo-dynamics-tranisotropic/#lagrangian-parcel","text":"The tutorial shows how to model the CPO evolution of a Lagrangian material parcel subject to three different modes of deformation :","title":"Lagrangian parcel"},{"location":"cpo-dynamics-tranisotropic/#lattice-rotation","text":"The strain-induced rotation of \\(c\\) -axes is modelled given the (local) velocity gradient tensor \\(\\nabla {\\bf u}\\) ( Svendsen and Hutter, 1996 ). The model is a kinematic model in the sense that \\(c\\) -axes rotate in response to the bulk rate of stretching, \\({\\bf D}\\) , and spin, \\({\\bf W}\\) , thereby allowing the detailed microscopic stress and strain rate fields to be neglected and hence interactions between neighboring grains to be disregarded. The modelled \\(c\\) -axes are taken to rotate with the bulk continuum spin ( \\({\\bf W}\\) ), plus some plastic spin correction ( \\({\\bf W}_{\\mathrm{p}}\\) ), so that the \\(c\\) -axis velocity field on the unit sphere is \\[ {\\bf \\dot{c}} = ({\\bf W} + {\\bf W}_{\\mathrm{p}}) \\cdot {\\hat {\\bf r}} \\quad\\text{($c$-axis velocity field on $S^2$)} , \\] where \\({\\hat {\\bf r}}\\) is the radial unit vector, and the plastic spin depends on \\({\\bf D}\\) to lowest and second lowest order following Wang (1969) and Aravas (1994) : \\[ {\\bf W}_{\\mathrm{p}} = \\iota({\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}\\cdot{\\bf D} - {\\bf D}\\cdot{\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}) + \\zeta({\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}\\cdot{\\bf D}^2 - {\\bf D}^2\\cdot{\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}) . \\] By requiring that basal planes preserve their orientation when subject to simple shear (like a deck of cards), it can be shown that \\(\\iota=1\\) and \\(\\zeta=0\\) . The corresponding effect on the continuous \\(c\\) -axis distribution is modelled as a conservative advection process on the surface of the unit sphere: \\[ \\frac{\\mathrm{D} n}{\\mathrm{D} t} = -\\nabla_{S^2}\\cdot(n{\\bf \\dot{c}}) \\quad\\Longrightarrow\\quad \\frac{\\mathrm{D} {\\bf s}}{\\mathrm{D} t} = {\\bf M_{\\mathrm{LROT}}} \\cdot {\\bf s}, \\] where \\({\\bf M_{\\mathrm{LROT}}}\\) is given analytically in Rathmann et al. (2021) . c-axis velocity field The normalized \\(c\\) -axis velocity fields for the three modes of deformation considered are:","title":"Lattice rotation"},{"location":"cpo-dynamics-tranisotropic/#example","text":"import numpy as np from specfabpy import specfab as sf # L=8 truncation is sufficient in this case, but larger L allows a very strong fabric to develop # and minimizes the effect that regularization has on low wavenumber modes (l=2,4) lm, nlm_len = sf.init(8) ### Velocity gradient tensor experienced by parcel ugrad = np.diag([0.5, 0.5, -1.0]) # Unconfined compression along z-direction (equal extension in x and y) D = (ugrad+np.transpose(ugrad))/2 # Symmetric part (strain-rate tensor) W = (ugrad-np.transpose(ugrad))/2 # Anti-symmetric part (spin tensor) ### Numerics Nt = 25 # Number of time steps dt = 0.05 # Time-step size ### Initialize fabric as isotropic nlm = np.zeros((Nt,nlm_len), dtype=np.complex64) # State vector (array of expansion coefficients) nlm[0,0] = 1/np.sqrt(4*np.pi) # Normalized ODF at t=0 ### Euler integration of lattice rotation + regularization for tt in np.arange(1,Nt): nlm_prev = nlm[tt-1,:] # Previous solution iota, zeta = 1, 0 # \"Deck of cards\" behavior M_LROT = sf.M_LROT(nlm_prev, D, W, iota, zeta) # Lattice rotation operator (nlm_len x nlm_len matrix) M_REG = sf.M_REG(nlm_prev, D) # Regularization operator (nlm_len x nlm_len matrix) M = M_LROT + M_REG nlm[tt,:] = nlm_prev + dt*np.matmul(M, nlm_prev) # Euler step nlm[tt,:] = sf.apply_bounds(nlm[tt,:]) # Apply spectral bounds if needed # See page \"Miscellaneous --> Plotting\" for how to plot resulting ODFs given nlm","title":"Example"},{"location":"cpo-dynamics-tranisotropic/#discontinous-dynamic-recrystallization-ddrx","text":"DDRX is modelled as a grain orientation or mass decay/production process on the unit sphere ( Placidi and others, 2010 ): \\[ \\frac{\\mathrm{D} n}{\\mathrm{D} t} = \\Gamma n \\quad\\Longrightarrow\\quad \\frac{\\mathrm{D} {\\bf s}}{\\mathrm{D} t} = {\\bf M_{\\mathrm{DDRX}}} \\cdot {\\bf s} , \\] where the decay/production rate \\[\\Gamma = \\Gamma_0\\left(D- {\\langle} D {\\rangle}\\right) \\quad\\text{(decay/production rate)}\\] depends on the rate magnitude \\(\\Gamma_0\\) , and the deformability \\(D\\) as a function of the stress tensor \\({\\bf S}\\) : \\[ D = \\frac{({\\bf S}\\cdot{\\bf S}):({\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}) - {\\bf S}:({\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}\\otimes{\\hat {\\bf r}}):{\\bf S}}{{\\bf S}:{\\bf S}}\\quad\\text{(deformability)} . \\] \\({\\bf M_{\\mathrm{DDRX}}}\\) is given analytically in Rathmann and Lilien (2021) . Note The average deformability, \\(\\langle D\\rangle\\) , depends on the instantaneous CPO state \u2014 specifically, the structure tensors a2 and a4 \u2014 making the corresponding matrix problem nonlinear by conserving the total number of grain orientations or mass density depending on how normalization is interpreted , the latter arguably resting on stronger physical grounds. Decay/production rate The normalized DDRX decay/production rate \\(\\Gamma/\\Gamma_0 = D - \\langle D \\rangle\\) is an orientation dependent field that favors nucleation (orientation/mass production) in the directions where the resolved basal-plane shear stress is maximal, and orientation/mass decay elsewhere. The the normalized decay rate for the three modes of deformation considered are:","title":"Discontinous dynamic recrystallization (DDRX)"},{"location":"cpo-dynamics-tranisotropic/#example_1","text":"import numpy as np from specfabpy import specfab as sf # L=8 truncation is sufficient in this case, but larger L allows a very strong fabric to develop # and minimizes the effect that regularization has on low wavenumber modes (l=2,4) lm, nlm_len = sf.init(8) ### Stress tensor experienced by parcel S = np.diag([0.0, 1.0, -1.0]) # Confined compression along z-direction (extension confined to y-direction) #S = np.diag([0.5, 0.5, -1.0]) # Unconfined compression along z-direction Gamma0 = 10 # DDRX decay-rate magnitude (may depend on temperature, strain-rate, and other factors, see e.g. Richards et al., 2021) ### Numerics Nt = 25 # Number of time steps dt = 0.05 # Time-step size ### Initialize fabric as isotropic nlm = np.zeros((Nt,nlm_len), dtype=np.complex64) # State vector (array of expansion coefficients) nlm[0,0] = 1/np.sqrt(4*np.pi) # Normalized ODF at t=0 ### Euler integration of DDRX for tt in np.arange(1,Nt): nlm_prev = nlm[tt-1,:] # Previous solution M = Gamma0 * sf.M_DDRX(nlm_prev, S) # DDRX operator (nlm_len x nlm_len matrix) nlm[tt,:] = nlm_prev + dt*np.matmul(M, nlm_prev) # Complete Euler step nlm[tt,:] = sf.apply_bounds(nlm[tt,:]) # Apply spectral bounds if needed # See page \"Miscellaneous --> Plotting\" for how to plot resulting ODFs given nlm","title":"Example"},{"location":"cpo-dynamics-tranisotropic/#continous-dynamic-recrystallization-cdrx","text":"Polygonization (rotation recrystallization, CDRX) accounts for the division of grains along internal sub-grain boundaries when exposed to bending stresses. In effect, CDRX reduces the average grain size upon grain division but does not necessarily change the CPO much ( Alley, 1992 ). The model follows G\u00f6dert (2003) by approximating the effect of CDRX on the distribution of grain orientations/mass as a Laplacian diffusive process on \\(S^2\\) : \\[ \\frac{\\mathrm{D} n}{\\mathrm{D} t} = \\Lambda\\nabla^2 n \\quad\\Longrightarrow\\quad \\frac{\\mathrm{D} {\\bf s}}{\\mathrm{D} t} = {\\bf M_{\\mathrm{CDRX}}} \\cdot {\\bf s} . \\]","title":"Continous dynamic recrystallization (CDRX)"},{"location":"cpo-dynamics-tranisotropic/#example_2","text":"To model CDRX, add the following contribution to the total fabric operator \\({\\bf M}\\) : M += Lambda*sf.M_CDRX(nlm) where Lambda is the CDRX rate-factor magnitude, \\(\\Lambda\\) , that possibly depends on temperature, stress, strain-rate, etc. ( Richards et al., 2021 ).","title":"Example"},{"location":"cpo-dynamics-tranisotropic/#regularization","text":"As \\(n(\\theta,\\phi)\\) becomes anisotropic due to CPO processes, the coefficients \\(n_l^m\\) associated with high wavenumber modes (large \\(l\\) and \\(m\\) , and thus small-scale structure) must increase in magnitude relative to the low wavenumber coefficients (small \\(l\\) and \\(m\\) ). One way to visualize this is by the angular power spectrum \\[ S(l) = \\frac{1}{2l + 1} \\sum_{m=-l}^l \\left\\vert n_l^m \\right\\vert^2 , \\] which grows with time. In the animation above, the left-hand panel shows how the power spectrum evolves under lattice rotation (unconfined vertical compression) compared to the end-member case of a delta function (dashed line). If the expansion series is truncated at \\(l=L\\) , then \\(l{\\gt}L\\) modes cannot evolve, and the truncated solution will reach an unphysical quasi-steady state. To prevent this, regularization must be introduced. Specfab uses Laplacian hyper diffusion ( \\(k>1\\) ) as regularization in \\(S^2\\) \\[ \\frac{\\mathrm{D} n_l^m}{\\mathrm{D} t} ={\\nu}[l(l+1)]^{k} n_l^m \\quad\\Longrightarrow\\quad \\frac{\\mathrm{D} {\\bf s}}{\\mathrm{D} t} = {\\bf M_{\\mathrm{REG}}} \\cdot {\\bf s} , \\] that can be added to the fabric evolution operator \\({\\bf M}\\) as follows: M += sf.M_REG(nlm, D) This allows the growth of high wavenumber modes to be disproportionately damped (green line compared to red line in animation above). Note As a rule-of-thumb, regularization affects the highest and next-highest modes \\(l{\\geq}L-2\\) and can therefore not be expected to evolve freely. This, in turn, means that structure tensors a2 and a4 , and hence calculated enhancement factors , might be affected by regularization unless \\(L{\\geq}8\\) is chosen.","title":"Regularization"},{"location":"cpo-dynamics-tranisotropic/#high-level-integrator","text":"Alternatively, you can use the high-level Lagrangian parcel integrator for constant stress/strain-rate: import numpy as np from specfabpy import specfab as sf from specfabpy import integrator as sfint lm, nlm_len = sf.init(8) ### Process rate factors etc. iota, zeta = 1, 0 # deck-of-cards behaviour for lattice rotation (=None => disabled) nu = 1 # scale the regularization magnitude by this amount (=None => no regularization) Gamma0 = Lambda = None # DDRX and CDRX rate factors (=None => disabled) ### Mode of deformation (mod) and parcel strain target # Note: axes 0,1,2 = x,y,z, planes 0,1,2 = yz,xz,xy # See page \"Miscellaneous --> Deformation Modes\" for T and r definitions mod, target = dict(type='ps', axis=2, T=+1, r=0), -0.95 # uniaxial compression along z until strain_zz = target mod, target = dict(type='ps', axis=2, T=-1, r=0), 6 # uniaxial extension along z until strain_zz = target mod, target = dict(type='ss', plane=1, T=+1), np.deg2rad(80) # simple shear until arctan(strain_xz) = target ### Integrate parcel CPO evolution nlm0 = np.zeros((nlm_len), dtype=np.complex64) nlm0[0] = 1/np.sqrt(4*np.pi) # normalized and isotropic initial state Nt = 200 # Number of integration steps nlm, F, time, ugrad = sfint.lagrangianparcel(sf, mod, target, Nt=Nt, nlm0=nlm0, iota=iota, zeta=zeta, Gamma0=Gamma0, Lambda=Lambda, nu=nu) # returns (state vector, deformation gradient tensor, time vector, velocity gradient) # See page \"Miscellaneous --> Plotting\" for how to plot resulting ODFs given nlm and parcel shape given F","title":"High-level integrator"},{"location":"cpo-dynamics-tranisotropic/#validation","text":"If the CPO is rotated into an approximately rotationally-symmetric frame about the \\(z\\) -axis, then only \\(n_l^0\\) components are nonzero. This conveniently allows validating modelled CPO processes by comparing modelled to observed correlations between, e.g., the lowest-order normalized components \\(\\hat{n}_2^0 = n_2^0/n_0^0\\) and \\(\\hat{n}_4^0 = n_4^0/n_0^0\\) . The below plot from Lilien et al. (2023) shows the observed correlation structure (markers) compared to the above CPO model(s) for different modes of deformation, suggesting that modelled CPO processes capture observations reasonably well.","title":"Validation"},{"location":"cpo-idealized/","text":"Idealized CPOs If concerned with the distribution of a single crystallographic axis, three types of idealized CPO states can be said to exist: Unidirectional CPO : all axes are perfectly aligned, i.e. perfect single maximum. Planar CPO : all axes are perfectly distributed on a plane, i.e. a great circle on \\(S^2\\) . Circle CPO : all axes are perfectly distributed on a small circle on \\(S^2\\) . Each of these can be expanded as a spherical harmonics series by using the sifting property of the delta function \\(\\delta({\\hat {\\bf r}})\\) . Unidirectional Consider the case where slip-plane normals are perfectly aligned with \\({{\\bf m}}\\) such that \\(n({\\hat {\\bf r}}) = \\delta({\\hat {\\bf r}}-{{\\bf m}})\\) . The corresponding expansion coefficients follow from the usual overlap integral: \\[ n_l^m = \\int_{S^2} \\delta(\\hat{{\\bf r}}-{{\\bf m}}) (Y_l^m(\\hat{{\\bf r}}))^* \\,\\mathrm{d}\\Omega = (Y_l^m({{\\bf m}}))^* . \\] In the figure below, the resulting unidirectional distribution is shown (rightmost inset), where the white area represents the subspace of possible CPOs when expressed in terms of the normalized coefficients of lowest order: \\(\\hat{n}_2^0 = n_2^0/n_0^0\\) and \\(\\hat{n}_4^0 = n_4^0/n_0^0\\) . The code below demonstrates how to generate the distribution with specfab. import numpy as np from specfabpy import specfab as sf L = 8 lm, nlm_len = sf.init(L) m = [0,0,1] # symmetry axis of distribution colat = 0 # 0 = unidirectional distribution, pi/2 = planar distribution, and anything in between is a small circle distribution nlm = sf.nlm_ideal(m, colat, L) # note: only l<=12 coefs are determined even if L>12 Planar and circle Planar and circle distributions follow from averaging the delta function over a desired co-latitude \\(\\theta\\) \u2014 i.e. the co-latitude where \\(n(\\hat{{\\bf r}})\\) should be sharply defined \u2014 in which case all zonal structure vanishes ( \\(m\\neq 0\\) components vanish) and we are left with \\[ n_l^m(\\theta) = \\begin{cases} Y_l^0(\\theta, \\phi=0) \\qquad\\text{if}\\quad m=0\\\\ 0 \\qquad\\qquad\\qquad\\quad \\text{if} \\quad m\\neq 0 \\end{cases} . \\] Here, \\({{\\bf m}}\\) is to be understood as the rotational symmetry axis of \\(n(\\hat{{\\bf r}})\\) , and the co-latitude is defined w.r.t. \\({{\\bf m}}\\) , not \\(\\hat{{\\bf z}}\\) . The above figure also shows the resulting \\(n(\\hat{{\\bf r}})\\) for different \\(\\theta\\) , calculated using the same code as above but for nonzero colat .","title":"Idealized states"},{"location":"cpo-idealized/#idealized-cpos","text":"If concerned with the distribution of a single crystallographic axis, three types of idealized CPO states can be said to exist: Unidirectional CPO : all axes are perfectly aligned, i.e. perfect single maximum. Planar CPO : all axes are perfectly distributed on a plane, i.e. a great circle on \\(S^2\\) . Circle CPO : all axes are perfectly distributed on a small circle on \\(S^2\\) . Each of these can be expanded as a spherical harmonics series by using the sifting property of the delta function \\(\\delta({\\hat {\\bf r}})\\) .","title":"Idealized CPOs"},{"location":"cpo-idealized/#unidirectional","text":"Consider the case where slip-plane normals are perfectly aligned with \\({{\\bf m}}\\) such that \\(n({\\hat {\\bf r}}) = \\delta({\\hat {\\bf r}}-{{\\bf m}})\\) . The corresponding expansion coefficients follow from the usual overlap integral: \\[ n_l^m = \\int_{S^2} \\delta(\\hat{{\\bf r}}-{{\\bf m}}) (Y_l^m(\\hat{{\\bf r}}))^* \\,\\mathrm{d}\\Omega = (Y_l^m({{\\bf m}}))^* . \\] In the figure below, the resulting unidirectional distribution is shown (rightmost inset), where the white area represents the subspace of possible CPOs when expressed in terms of the normalized coefficients of lowest order: \\(\\hat{n}_2^0 = n_2^0/n_0^0\\) and \\(\\hat{n}_4^0 = n_4^0/n_0^0\\) . The code below demonstrates how to generate the distribution with specfab. import numpy as np from specfabpy import specfab as sf L = 8 lm, nlm_len = sf.init(L) m = [0,0,1] # symmetry axis of distribution colat = 0 # 0 = unidirectional distribution, pi/2 = planar distribution, and anything in between is a small circle distribution nlm = sf.nlm_ideal(m, colat, L) # note: only l<=12 coefs are determined even if L>12","title":"Unidirectional"},{"location":"cpo-idealized/#planar-and-circle","text":"Planar and circle distributions follow from averaging the delta function over a desired co-latitude \\(\\theta\\) \u2014 i.e. the co-latitude where \\(n(\\hat{{\\bf r}})\\) should be sharply defined \u2014 in which case all zonal structure vanishes ( \\(m\\neq 0\\) components vanish) and we are left with \\[ n_l^m(\\theta) = \\begin{cases} Y_l^0(\\theta, \\phi=0) \\qquad\\text{if}\\quad m=0\\\\ 0 \\qquad\\qquad\\qquad\\quad \\text{if} \\quad m\\neq 0 \\end{cases} . \\] Here, \\({{\\bf m}}\\) is to be understood as the rotational symmetry axis of \\(n(\\hat{{\\bf r}})\\) , and the co-latitude is defined w.r.t. \\({{\\bf m}}\\) , not \\(\\hat{{\\bf z}}\\) . The above figure also shows the resulting \\(n(\\hat{{\\bf r}})\\) for different \\(\\theta\\) , calculated using the same code as above but for nonzero colat .","title":"Planar and circle"},{"location":"cpo-representation/","text":"CPO representation CPOs are represented by their distributions of crystallographic axes in orientation space ( \\(S^2\\) ), neglecting grain sizes/mass and other topological information. Supported grain symmetry groups are: Grain symmetry CPO components Definition Transversely isotropic \\(n(\\theta,\\phi)\\) Distribution of slip-plane normals Orthotropic \\(n(\\theta,\\phi),\\,b(\\theta,\\phi)\\) Distribution of slip-plane normals and slip directions Thus, depending on which crystallographic slip system is preferentially activated, \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) may refer to the distributions of different crystallographic axes. Glacier ice Since ice grains are approximately transversely isotropic, tracking \\(n(\\theta,\\phi)\\) (the \\(c\\) -axis distribution) is sufficient for representing the CPO. Polycrystalline ice Ensemble of slip elements Olivine For orthotropic grains such as olivine, both \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) distributions must be tracked to represent the CPO. Polycrystalline olivine Ensemble of slip elements Note that \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) represent the distributions of particular crystallographic axes ( \\({\\bf m}'_i\\) ) depending on fabric type (A\u2014E type). ODF The orientation distribution function (ODF) of a given slip-system axis (crystallographic axis) \\(f\\in \\lbrace n,b\\rbrace\\) is defined as the normalized distribution \\[ \\mathrm{ODF} = \\frac{f(\\theta,\\phi)}{N} \\quad\\text{where}\\quad N=\\int_{S^2} f(\\theta,\\phi) \\,\\mathrm{d}\\Omega . \\] Normalization \\(n(\\theta,\\phi)\\) may be understood either as the number density of grains with a given slip-plane normal orientation, or as the mass density fraction ( Faria, 2006 ; Richards et al., 2021 ) of grains with a given slip-plane normal orientation; \\(\\varrho^*(\\theta,\\phi)\\) in literature. The same goes for \\(b(\\theta,\\phi)\\) . From specfab's point-of-view, the difference is a matter of normalization: since the models of CPO evolution (lattice rotation, DDRX, CDRX) conserve the normalization, the two views are effectively the same, not least because CPO-derived quantities depend on the normalized distributions (which are identical). The mass-density-fraction interpretation rests, however, on stronger physical grounds as mass is conserved but grain numbers are not. Harmonic expansion The distributions \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) are represented as spherical harmonic expansion series: \\[ n(\\theta,\\phi)=\\sum_{l=0}^{L}\\sum_{m=-l}^{l}n_{l}^{m}Y_{l}^{m}(\\theta,\\phi) \\quad\\text{(distribution of slip-plane normals)}, \\] \\[ b(\\theta,\\phi)=\\sum_{l=0}^{L}\\sum_{m=-l}^{l}b_{l}^{m}Y_{l}^{m}(\\theta,\\phi) \\quad\\text{(distribution of slip directions)}. \\] The CPO state is thus described by the state vectors of complex-valued expansion coefficients \\[ {\\bf s}_n = [n_0^0,n_2^{-2},n_2^{-1},n_2^{0},n_2^{1},n_2^{2},n_4^{-4},\\cdots,n_4^{4},\\cdots,n_L^{-L},\\cdots,n_L^{L}] \\quad\\text{($n$ state vector)}, \\] \\[ {\\bf s}_b = [b_0^0,b_2^{-2},b_2^{-1},b_2^{0},b_2^{1},b_2^{2},b_4^{-4},\\cdots,b_4^{4},\\cdots,b_L^{-L},\\cdots,b_L^{L}] \\quad\\text{($b$ state vector)}, \\] where the magnitude and complex phase of the coefficients determine the size and rotation of the contribution from the associated harmonic mode. Reduced form Not all expansion coefficients are independent for real-valued expansion series, but must fulfill (likewise for \\(b\\) ) \\[ n_l^{-m}=(-1)^m(n_l^m)^* . \\] This can be taken advantage of for large problems where many (e.g. gridded) CPOs must be stored in memory, thereby effectively reducing the size of the problem. The vector of reduced expansion coefficients is defined as \\[ \\tilde{{\\bf s}}= [n_0^0,n_2^{0},n_2^{1},n_2^{2},n_4^{0},\\cdots,n_4^{4},\\cdots,n_L^{0},\\cdots,n_L^{L}] \\quad\\text{(reduced state vector)}. \\] Converting between full and reduced forms is done as follows: import numpy as np from specfabpy import specfabpy as sf lm, nlm_len = sf.init(2) # L=2 truncation is sufficient in this case ### Construct an arbitrary fabric a2 = np.diag([0.1,0.2,0.7]) # arbitrary second-order structure tensor nlm = np.zeros((nlm_len), dtype=np.complex64) # array of expansion coefficients nlm[:sf.L2len] = sf.a2_to_nlm(a2) # determine l<=2 expansion coefficients of ODF print('original:', nlm) ### Get reduced form of coefficient array, rnlm rnlm_len = sf.get_rnlm_len() rnlm = np.zeros((rnlm_len), dtype=np.complex64) # array of reduced expansion coefficients rnlm[:] = sf.nlm_to_rnlm(nlm, rnlm_len) # reduced form print('reduced:', rnlm) ### Recover full form (nlm) from reduced form (rnlm) nlm[:] = sf.rnlm_to_nlm(rnlm, nlm_len) print('recovered:', nlm)","title":"Representation"},{"location":"cpo-representation/#cpo-representation","text":"CPOs are represented by their distributions of crystallographic axes in orientation space ( \\(S^2\\) ), neglecting grain sizes/mass and other topological information. Supported grain symmetry groups are: Grain symmetry CPO components Definition Transversely isotropic \\(n(\\theta,\\phi)\\) Distribution of slip-plane normals Orthotropic \\(n(\\theta,\\phi),\\,b(\\theta,\\phi)\\) Distribution of slip-plane normals and slip directions Thus, depending on which crystallographic slip system is preferentially activated, \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) may refer to the distributions of different crystallographic axes. Glacier ice Since ice grains are approximately transversely isotropic, tracking \\(n(\\theta,\\phi)\\) (the \\(c\\) -axis distribution) is sufficient for representing the CPO. Polycrystalline ice Ensemble of slip elements Olivine For orthotropic grains such as olivine, both \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) distributions must be tracked to represent the CPO. Polycrystalline olivine Ensemble of slip elements Note that \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) represent the distributions of particular crystallographic axes ( \\({\\bf m}'_i\\) ) depending on fabric type (A\u2014E type).","title":"CPO representation"},{"location":"cpo-representation/#odf","text":"The orientation distribution function (ODF) of a given slip-system axis (crystallographic axis) \\(f\\in \\lbrace n,b\\rbrace\\) is defined as the normalized distribution \\[ \\mathrm{ODF} = \\frac{f(\\theta,\\phi)}{N} \\quad\\text{where}\\quad N=\\int_{S^2} f(\\theta,\\phi) \\,\\mathrm{d}\\Omega . \\]","title":"ODF"},{"location":"cpo-representation/#normalization","text":"\\(n(\\theta,\\phi)\\) may be understood either as the number density of grains with a given slip-plane normal orientation, or as the mass density fraction ( Faria, 2006 ; Richards et al., 2021 ) of grains with a given slip-plane normal orientation; \\(\\varrho^*(\\theta,\\phi)\\) in literature. The same goes for \\(b(\\theta,\\phi)\\) . From specfab's point-of-view, the difference is a matter of normalization: since the models of CPO evolution (lattice rotation, DDRX, CDRX) conserve the normalization, the two views are effectively the same, not least because CPO-derived quantities depend on the normalized distributions (which are identical). The mass-density-fraction interpretation rests, however, on stronger physical grounds as mass is conserved but grain numbers are not.","title":"Normalization"},{"location":"cpo-representation/#harmonic-expansion","text":"The distributions \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) are represented as spherical harmonic expansion series: \\[ n(\\theta,\\phi)=\\sum_{l=0}^{L}\\sum_{m=-l}^{l}n_{l}^{m}Y_{l}^{m}(\\theta,\\phi) \\quad\\text{(distribution of slip-plane normals)}, \\] \\[ b(\\theta,\\phi)=\\sum_{l=0}^{L}\\sum_{m=-l}^{l}b_{l}^{m}Y_{l}^{m}(\\theta,\\phi) \\quad\\text{(distribution of slip directions)}. \\] The CPO state is thus described by the state vectors of complex-valued expansion coefficients \\[ {\\bf s}_n = [n_0^0,n_2^{-2},n_2^{-1},n_2^{0},n_2^{1},n_2^{2},n_4^{-4},\\cdots,n_4^{4},\\cdots,n_L^{-L},\\cdots,n_L^{L}] \\quad\\text{($n$ state vector)}, \\] \\[ {\\bf s}_b = [b_0^0,b_2^{-2},b_2^{-1},b_2^{0},b_2^{1},b_2^{2},b_4^{-4},\\cdots,b_4^{4},\\cdots,b_L^{-L},\\cdots,b_L^{L}] \\quad\\text{($b$ state vector)}, \\] where the magnitude and complex phase of the coefficients determine the size and rotation of the contribution from the associated harmonic mode.","title":"Harmonic expansion"},{"location":"cpo-representation/#reduced-form","text":"Not all expansion coefficients are independent for real-valued expansion series, but must fulfill (likewise for \\(b\\) ) \\[ n_l^{-m}=(-1)^m(n_l^m)^* . \\] This can be taken advantage of for large problems where many (e.g. gridded) CPOs must be stored in memory, thereby effectively reducing the size of the problem. The vector of reduced expansion coefficients is defined as \\[ \\tilde{{\\bf s}}= [n_0^0,n_2^{0},n_2^{1},n_2^{2},n_4^{0},\\cdots,n_4^{4},\\cdots,n_L^{0},\\cdots,n_L^{L}] \\quad\\text{(reduced state vector)}. \\] Converting between full and reduced forms is done as follows: import numpy as np from specfabpy import specfabpy as sf lm, nlm_len = sf.init(2) # L=2 truncation is sufficient in this case ### Construct an arbitrary fabric a2 = np.diag([0.1,0.2,0.7]) # arbitrary second-order structure tensor nlm = np.zeros((nlm_len), dtype=np.complex64) # array of expansion coefficients nlm[:sf.L2len] = sf.a2_to_nlm(a2) # determine l<=2 expansion coefficients of ODF print('original:', nlm) ### Get reduced form of coefficient array, rnlm rnlm_len = sf.get_rnlm_len() rnlm = np.zeros((rnlm_len), dtype=np.complex64) # array of reduced expansion coefficients rnlm[:] = sf.nlm_to_rnlm(nlm, rnlm_len) # reduced form print('reduced:', rnlm) ### Recover full form (nlm) from reduced form (rnlm) nlm[:] = sf.rnlm_to_nlm(rnlm, nlm_len) print('recovered:', nlm)","title":"Reduced form"},{"location":"cpo-rotation/","text":"Rotation Rotating an expansion series by \\(\\theta\\) about the \\(y\\) -axis (in the \\(x\\) \u2014 \\(z\\) plane) followed by \\(\\phi\\) about the \\(z\\) -axis (in the \\(x\\) \u2014 \\(y\\) plane) can be done as follow: import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(8) ### Construct an arbitrary fabric to rotate a2 = np.diag([0, 0, 1]) # arbitrary second-order structure tensor, a^(2) nlm = np.zeros((nlm_len), dtype=np.complex64) # array of expansion coefficients nlm[:sf.L2len] = sf.a2_to_nlm(a2) # l<=2 expansion coefficients of corresponding ODF ### Rotate ODF # Note: assumes L=<12 (rotation for larger L is not implemented) theta = np.deg2rad(-45) phi = np.deg2rad(45) nlm_rot1 = sf.rotate_nlm(nlm, theta, 0) # first rotate around y axis in x-z plane nlm_rot2 = sf.rotate_nlm(nlm_rot1, 0, phi) # next rotate around z axis in x-y plane nlm_rot3 = sf.rotate_nlm(nlm_rot2, -theta, -phi) # rotate back # See \"plotting\" pages on how to plot the resulting ODFs","title":"Rotation"},{"location":"cpo-rotation/#rotation","text":"Rotating an expansion series by \\(\\theta\\) about the \\(y\\) -axis (in the \\(x\\) \u2014 \\(z\\) plane) followed by \\(\\phi\\) about the \\(z\\) -axis (in the \\(x\\) \u2014 \\(y\\) plane) can be done as follow: import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(8) ### Construct an arbitrary fabric to rotate a2 = np.diag([0, 0, 1]) # arbitrary second-order structure tensor, a^(2) nlm = np.zeros((nlm_len), dtype=np.complex64) # array of expansion coefficients nlm[:sf.L2len] = sf.a2_to_nlm(a2) # l<=2 expansion coefficients of corresponding ODF ### Rotate ODF # Note: assumes L=<12 (rotation for larger L is not implemented) theta = np.deg2rad(-45) phi = np.deg2rad(45) nlm_rot1 = sf.rotate_nlm(nlm, theta, 0) # first rotate around y axis in x-z plane nlm_rot2 = sf.rotate_nlm(nlm_rot1, 0, phi) # next rotate around z axis in x-y plane nlm_rot3 = sf.rotate_nlm(nlm_rot2, -theta, -phi) # rotate back # See \"plotting\" pages on how to plot the resulting ODFs","title":"Rotation"},{"location":"cpo-structuretensors/","text":"Structure tensors The \\(k\\) -th order structure tensor (vector moment) is defined as the average \\(k\\) -th repeated outer product of a slip-system axis (crystallographic axis) with itself. For example, in the case of a discrete ensemble of slip plane normals (e.g. \\({\\bf n} = {\\bf c}\\) for ice) they are \\[ {\\bf a}^{(k)}({\\bf n}_i) = \\frac{1}{N}\\sum_i^N ({\\bf n}_i\\otimes)^k, \\] where \\(N\\) is the total number of grains, assuming equal grain weight (i.e. mass) for simplicity. Alternatively, if the distribution function of \\({\\bf n}\\) axes is known, i.e. \\(n(\\theta,\\phi)\\) , the structure tensors are \\[ {\\bf a}^{(k)}(n) = \\frac{1}{N} \\int_{S^2} (\\hat{{\\bf r}}\\otimes)^k n(\\theta,\\phi) \\, \\mathrm{d}\\Omega , \\] where \\(\\mathrm{d}\\Omega = \\sin(\\theta) \\mathrm{d}\\theta \\mathrm{d}\\phi\\) is the infinitesimal solid angle, \\(\\hat{{\\bf r}}(\\theta,\\phi)\\) is the radial unit vector, and \\(N=\\int_{S^2} n(\\theta,\\phi) \\, \\mathrm{d}\\Omega\\) . Principal frame Since \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) are antipodally symmetric, odd moments (odd \\(k\\) ) vanish identically. Hence, \\({\\bf a}^{(2)}(n)\\) and \\({\\bf a}^{(2)}(b)\\) measure the variance of \\(\\bf n\\) and \\(\\bf b\\) axes, respectively, around the three coordinate axes. Posing \\({\\bf a}^{(2)}\\) in its principal frame \\[ {\\bf a}^{(2)} = \\left[\\begin{matrix} \\lambda_1 & 0 & 0\\\\ 0 & \\lambda_2 & 0\\\\ 0 & 0 & \\lambda_3\\\\ \\end{matrix}\\right] \\] therefore has a similar interpretation as in PCA: the first principal component (eigenvector \\({\\bf m}_1\\) ) is the direction that maximizes the variance (eigenvalue \\(\\lambda_1\\) ) of the projected data (red curve), the second component is the direction orthogonal to the first component that maximizes the variance of the projected data, and so on with the third component. Convert to spectral Converting between spectral and tensorial representations is a linear problem in the sense that \\[ {\\bf a}^{(k)}(n) = {\\bf f}(\\hat{n}_2^{m}, \\hat{n}_4^{m}, \\cdots, \\hat{n}_k^{m}) , \\qquad\\text{(for all $m$)} \\] where \\({\\bf f}\\) is linear in its arguments, and \\[ \\hat{n}_l^m = n_l^m/n_0^0 . \\] In the case of \\({\\bf a}^{(2)}\\) the relation is simple: \\[ {\\bf a}^{(2)} = \\frac{{\\bf I}}{3} + \\sqrt{\\frac{2}{15}} \\left[\\begin{matrix} \\operatorname{Re}[\\hat{n}_2^2] - \\dfrac{1}{2}\\sqrt{\\dfrac{2}{3}} \\hat{n}_2^0 & -\\operatorname{Im}[\\hat{n}_2^2] & -\\operatorname{Re}[\\hat{n}_2^1] \\\\ & -\\operatorname{Re}[\\hat{n}_2^2] - \\dfrac{1}{2}\\sqrt{\\dfrac{2}{3}} \\hat{n}_2^0 & \\operatorname{Im}[\\hat{n}_2^1] \\\\ \\mathrm{sym.} & & \\sqrt{\\dfrac{2}{3}} \\hat{n}_2^0 \\end{matrix}\\right] , \\] but for higher-order structure tensors the expressions are long (not shown). The above applies to \\(b(\\theta,\\phi)\\) as well. The following code example shows how to convert between the representations: import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(8) nlm = np.zeros((nlm_len), dtype=np.complex64) # array of expansion coefficients ### a2 to nlm a2 = np.diag([0.0,0.25,0.75]) # arbitrary second-order structure tensor nlm[:sf.L2len] = sf.a2_to_nlm(a2) # determine l<=2 expansion coefficients of ODF a2 = sf.a2(nlm) # nlm back to a2 print('a2 is: ', a2) ### a4 to nlm p = np.array([0,0,1]) # unidirectional CPO a4 = np.einsum('i,j,k,l', p,p,p,p) # a4 for ODF = deltafunc(r-p) nlm[:sf.L4len] = sf.a4_to_nlm(a4) # determine l<=4 expansion coefficients of ODF a4 = sf.a4(nlm) # nlm back to a4 print('a4 is: ', a4) ### a6 to nlm p = np.array([0,0,1]) # unidirectional CPO a6 = np.einsum('i,j,k,l,m,n', p,p,p,p,p,p) # a6 for ODF = deltafunc(r-p) nlm[:sf.L6len] = sf.a6_to_nlm(a6) # determine l<=6 expansion coefficients of ODF a6 = sf.a6(nlm) # nlm back to a6 print('a6 is: ', a6) Construct from measurements The spectral expansion coefficients of any CPO may be determined from discrete measurements of crystallographic axes. This requires constructing the corresponding structure tensors (for each crystallographic axis), from which the expansion coefficients may be derived. import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(8) ### Replace with your own array/list of measured c-axes # caxes = [[c1x,c1y,c1z], [c2x,c2y,c2z], ...] ### Determine sixth-order structure tensor, a6 a6 = np.zeros((3,3,3,3,3,3)) for c in caxes a6 += np.einsum('i,j,k,l,m,n', c,c,c,c,c,c) # sixth outer product of c-axis with itself a6 /= len(caxes) # normalize by number of c-axes (grains) ### Determine spectral expansion coefficients nlm = np.zeros((nlm_len), dtype=np.complex64) # array of expansion coefficients nlm[:sf.L6len] = sf.a6_to_nlm(a6) # determine l<=6 expansion coefficients of ODF Note that constructing a6 is to be preferred over a4 and a2 since it contains more information on the fine-scale structure of the distribution; that is, \\(l\\leq 6\\) expansion coefficients as opposed to \\(l\\leq 4\\) and \\(l\\leq 2\\) coefficients, respectively.","title":"Structure tensors"},{"location":"cpo-structuretensors/#structure-tensors","text":"The \\(k\\) -th order structure tensor (vector moment) is defined as the average \\(k\\) -th repeated outer product of a slip-system axis (crystallographic axis) with itself. For example, in the case of a discrete ensemble of slip plane normals (e.g. \\({\\bf n} = {\\bf c}\\) for ice) they are \\[ {\\bf a}^{(k)}({\\bf n}_i) = \\frac{1}{N}\\sum_i^N ({\\bf n}_i\\otimes)^k, \\] where \\(N\\) is the total number of grains, assuming equal grain weight (i.e. mass) for simplicity. Alternatively, if the distribution function of \\({\\bf n}\\) axes is known, i.e. \\(n(\\theta,\\phi)\\) , the structure tensors are \\[ {\\bf a}^{(k)}(n) = \\frac{1}{N} \\int_{S^2} (\\hat{{\\bf r}}\\otimes)^k n(\\theta,\\phi) \\, \\mathrm{d}\\Omega , \\] where \\(\\mathrm{d}\\Omega = \\sin(\\theta) \\mathrm{d}\\theta \\mathrm{d}\\phi\\) is the infinitesimal solid angle, \\(\\hat{{\\bf r}}(\\theta,\\phi)\\) is the radial unit vector, and \\(N=\\int_{S^2} n(\\theta,\\phi) \\, \\mathrm{d}\\Omega\\) .","title":"Structure tensors"},{"location":"cpo-structuretensors/#principal-frame","text":"Since \\(n(\\theta,\\phi)\\) and \\(b(\\theta,\\phi)\\) are antipodally symmetric, odd moments (odd \\(k\\) ) vanish identically. Hence, \\({\\bf a}^{(2)}(n)\\) and \\({\\bf a}^{(2)}(b)\\) measure the variance of \\(\\bf n\\) and \\(\\bf b\\) axes, respectively, around the three coordinate axes. Posing \\({\\bf a}^{(2)}\\) in its principal frame \\[ {\\bf a}^{(2)} = \\left[\\begin{matrix} \\lambda_1 & 0 & 0\\\\ 0 & \\lambda_2 & 0\\\\ 0 & 0 & \\lambda_3\\\\ \\end{matrix}\\right] \\] therefore has a similar interpretation as in PCA: the first principal component (eigenvector \\({\\bf m}_1\\) ) is the direction that maximizes the variance (eigenvalue \\(\\lambda_1\\) ) of the projected data (red curve), the second component is the direction orthogonal to the first component that maximizes the variance of the projected data, and so on with the third component.","title":"Principal frame"},{"location":"cpo-structuretensors/#convert-to-spectral","text":"Converting between spectral and tensorial representations is a linear problem in the sense that \\[ {\\bf a}^{(k)}(n) = {\\bf f}(\\hat{n}_2^{m}, \\hat{n}_4^{m}, \\cdots, \\hat{n}_k^{m}) , \\qquad\\text{(for all $m$)} \\] where \\({\\bf f}\\) is linear in its arguments, and \\[ \\hat{n}_l^m = n_l^m/n_0^0 . \\] In the case of \\({\\bf a}^{(2)}\\) the relation is simple: \\[ {\\bf a}^{(2)} = \\frac{{\\bf I}}{3} + \\sqrt{\\frac{2}{15}} \\left[\\begin{matrix} \\operatorname{Re}[\\hat{n}_2^2] - \\dfrac{1}{2}\\sqrt{\\dfrac{2}{3}} \\hat{n}_2^0 & -\\operatorname{Im}[\\hat{n}_2^2] & -\\operatorname{Re}[\\hat{n}_2^1] \\\\ & -\\operatorname{Re}[\\hat{n}_2^2] - \\dfrac{1}{2}\\sqrt{\\dfrac{2}{3}} \\hat{n}_2^0 & \\operatorname{Im}[\\hat{n}_2^1] \\\\ \\mathrm{sym.} & & \\sqrt{\\dfrac{2}{3}} \\hat{n}_2^0 \\end{matrix}\\right] , \\] but for higher-order structure tensors the expressions are long (not shown). The above applies to \\(b(\\theta,\\phi)\\) as well. The following code example shows how to convert between the representations: import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(8) nlm = np.zeros((nlm_len), dtype=np.complex64) # array of expansion coefficients ### a2 to nlm a2 = np.diag([0.0,0.25,0.75]) # arbitrary second-order structure tensor nlm[:sf.L2len] = sf.a2_to_nlm(a2) # determine l<=2 expansion coefficients of ODF a2 = sf.a2(nlm) # nlm back to a2 print('a2 is: ', a2) ### a4 to nlm p = np.array([0,0,1]) # unidirectional CPO a4 = np.einsum('i,j,k,l', p,p,p,p) # a4 for ODF = deltafunc(r-p) nlm[:sf.L4len] = sf.a4_to_nlm(a4) # determine l<=4 expansion coefficients of ODF a4 = sf.a4(nlm) # nlm back to a4 print('a4 is: ', a4) ### a6 to nlm p = np.array([0,0,1]) # unidirectional CPO a6 = np.einsum('i,j,k,l,m,n', p,p,p,p,p,p) # a6 for ODF = deltafunc(r-p) nlm[:sf.L6len] = sf.a6_to_nlm(a6) # determine l<=6 expansion coefficients of ODF a6 = sf.a6(nlm) # nlm back to a6 print('a6 is: ', a6)","title":"Convert to spectral"},{"location":"cpo-structuretensors/#construct-from-measurements","text":"The spectral expansion coefficients of any CPO may be determined from discrete measurements of crystallographic axes. This requires constructing the corresponding structure tensors (for each crystallographic axis), from which the expansion coefficients may be derived. import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(8) ### Replace with your own array/list of measured c-axes # caxes = [[c1x,c1y,c1z], [c2x,c2y,c2z], ...] ### Determine sixth-order structure tensor, a6 a6 = np.zeros((3,3,3,3,3,3)) for c in caxes a6 += np.einsum('i,j,k,l,m,n', c,c,c,c,c,c) # sixth outer product of c-axis with itself a6 /= len(caxes) # normalize by number of c-axes (grains) ### Determine spectral expansion coefficients nlm = np.zeros((nlm_len), dtype=np.complex64) # array of expansion coefficients nlm[:sf.L6len] = sf.a6_to_nlm(a6) # determine l<=6 expansion coefficients of ODF Note that constructing a6 is to be preferred over a4 and a2 since it contains more information on the fine-scale structure of the distribution; that is, \\(l\\leq 6\\) expansion coefficients as opposed to \\(l\\leq 4\\) and \\(l\\leq 2\\) coefficients, respectively.","title":"Construct from measurements"},{"location":"deformation-modes/","text":"Deformation modes For a continuum subject to deformation, the deformation gradient tensor , \\({\\bf F}\\) , describes the relative change in position of material points. If \\({\\bf F}\\) is known, then the velocity gradient tensor follows as $$ \\nabla {\\bf u} = \\dot{{\\bf F}} {\\bf F}^{-1} . $$ The resulting strain experienced (strain tensor) is \\begin{align} {\\boldsymbol \\epsilon} = \\frac{1}{2}\\left( {\\bf F}+{\\bf F}^\\top \\right) - {\\bf I} . \\end{align} Below, we consider how to represent pure shear and simple shear with \\({\\bf F}\\) . Pure shear \\({\\bf F}\\) is diagonal for pure shear deformation when the principal strain axes are aligned with the coordinate system. Suppose shortening takes places along the vertical axis and lengthening in the horizontal plane, then \\[ {\\bf F}_{\\mathrm{P}} = \\begin{bmatrix} b^{(1+r)/2} & 0 & 0\\\\ 0 & b^{(1-r)/2} & 0\\\\ 0& 0& b^{-1} \\end{bmatrix} , \\] where the parameter \\(r\\in[-1;1]\\) controls the relative lengthening between two horizontal directions: for \\(r=0\\) lengthening is equal in the \\(x\\) and \\(y\\) directions, for \\(r=+1\\) lengthening occurs only in the \\(x\\) direction, for \\(r=-1\\) lengthening occurs only in the \\(y\\) direction. Calculating the velocity gradient tensor yields \\[ \\nabla {\\bf u} = \\frac{\\dot{b}}{b} \\begin{bmatrix} (1+r)/2 & 0 & 0\\\\ 0 & (1-r)/2 & 0\\\\ 0& 0& -1 \\end{bmatrix} . \\] If the scaling parameter, \\(b\\) , is written in terms of the \\(e\\) -folding time scale \\(T\\) as \\[ b(t) = \\exp(t/T) , \\] the strain-rate becomes constant, \\[ \\frac{\\dot{b}}{b} = \\frac{1}{T}. \\] Notice that the vertical strain experienced as a function of time is \\[ \\epsilon_{zz}(t) = \\frac{1}{b(t)} - 1, \\] where \\(\\epsilon_{zz} = 0\\) corresponds to an undeformed ice parcel ( \\(t=0\\) ), and \\(\\epsilon_{zz} = -1\\) is the limit of vanishing parcel height ( \\(t\\rightarrow\\infty\\) ). Consider instead the case where lengthening takes place along the vertical axis and shortening in the horizontal plane. This is achieved by substituting \\(b\\rightarrow b^{-1}\\) which implies \\({\\bf F}={\\bf F}_{\\mathrm{P}}^{-1}\\) and hence is the time reversed behavior of \\({\\bf F}_{\\mathrm{P}}\\) since \\(b^{-1}(t)=b(-t)\\) . Example The above expressions are accessible in specfab as follows import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(4) axis = 2 # axis of shortening (T>0) or lengthening (T<0): 0=x, 1=y, 2=z Tc = 1 # time taken in seconds for parcel to reduce to half (50%) height if T>0, or abs(time) taken for parcel to double in height (200%) if T<0. r = 0 # asymmetry parameter for shortening (if T>0) or lengthening (if T<0) T = Tc/np.log(2) # corresponding e-folding time ugrad = sf.pureshear_ugrad(axis, r, T) # velocity gradient D, W = sf.ugrad_to_D_and_W(ugrad) # strain-rate and spin tensor t = 1 # some specific time of interest b = sf.pureshear_b(T, t) # scaling parameter at time t F = sf.pureshear_F(axis, r, T, t) # deformation gradient tensor at time t eps = sf.F_to_strain(F) # strain tensor at time t Simple shear Simple shear strain may be characterized by the shear angle \\(\\gamma\\) of the resulting rhombus. In the case of vertical shear in the \\(x\\) \u2014 \\(z\\) plane, the deformation gradient tensor is given by \\[ {\\bf F}_{\\mathrm{S}} = \\begin{bmatrix} 1 & 0 & \\tan(\\gamma) \\\\ 0 & 1 & 0\\\\ 0& 0& 1 \\end{bmatrix} , \\] and velocity-gradient tensor becomes \\[ \\nabla {\\bf u} = \\frac{\\dot{\\gamma}}{\\cos^2(\\gamma)} \\begin{bmatrix} 0 & 0 & 1\\\\ 0 & 0 & 0\\\\ 0& 0& 0 \\end{bmatrix} . \\] For a constant shear rate, \\(1/T\\) , the shear-angle time dependence is \\begin{align} \\gamma(t) = \\tan^{-1}(t/T) , \\end{align} where \\(T\\) is the characteristic time taken to reach a shear of 1 from an undeformed state. In this case, \\(\\nabla {\\bf u}\\) is constant, too, since \\[ \\frac{\\dot{\\gamma}}{\\cos^2(\\gamma)} = \\frac{1}{T} . \\] Example The above expressions are accessible in specfab as follows import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(4) plane = 1 # plane of shear: 0=yz, 1=xz, 2=xy T = 1 # time taken in seconds for parcel to a reach shear strain of 1 (45 deg shear angle) ugrad = sf.simpleshear_ugrad(plane, T) # velocity gradient D, W = sf.ugrad_to_D_and_W(ugrad) # strain-rate and spin tensor t = 1 # some specific time of interest gamma = sf.simpleshear_gamma(T, t) # shear angle at time t F = sf.simpleshear_F(plane, T, t) # deformation gradient tensor at time t eps = sf.F_to_strain(F) # strain tensor at time t","title":"Deformation modes"},{"location":"deformation-modes/#deformation-modes","text":"For a continuum subject to deformation, the deformation gradient tensor , \\({\\bf F}\\) , describes the relative change in position of material points. If \\({\\bf F}\\) is known, then the velocity gradient tensor follows as $$ \\nabla {\\bf u} = \\dot{{\\bf F}} {\\bf F}^{-1} . $$ The resulting strain experienced (strain tensor) is \\begin{align} {\\boldsymbol \\epsilon} = \\frac{1}{2}\\left( {\\bf F}+{\\bf F}^\\top \\right) - {\\bf I} . \\end{align} Below, we consider how to represent pure shear and simple shear with \\({\\bf F}\\) .","title":"Deformation modes"},{"location":"deformation-modes/#pure-shear","text":"\\({\\bf F}\\) is diagonal for pure shear deformation when the principal strain axes are aligned with the coordinate system. Suppose shortening takes places along the vertical axis and lengthening in the horizontal plane, then \\[ {\\bf F}_{\\mathrm{P}} = \\begin{bmatrix} b^{(1+r)/2} & 0 & 0\\\\ 0 & b^{(1-r)/2} & 0\\\\ 0& 0& b^{-1} \\end{bmatrix} , \\] where the parameter \\(r\\in[-1;1]\\) controls the relative lengthening between two horizontal directions: for \\(r=0\\) lengthening is equal in the \\(x\\) and \\(y\\) directions, for \\(r=+1\\) lengthening occurs only in the \\(x\\) direction, for \\(r=-1\\) lengthening occurs only in the \\(y\\) direction. Calculating the velocity gradient tensor yields \\[ \\nabla {\\bf u} = \\frac{\\dot{b}}{b} \\begin{bmatrix} (1+r)/2 & 0 & 0\\\\ 0 & (1-r)/2 & 0\\\\ 0& 0& -1 \\end{bmatrix} . \\] If the scaling parameter, \\(b\\) , is written in terms of the \\(e\\) -folding time scale \\(T\\) as \\[ b(t) = \\exp(t/T) , \\] the strain-rate becomes constant, \\[ \\frac{\\dot{b}}{b} = \\frac{1}{T}. \\] Notice that the vertical strain experienced as a function of time is \\[ \\epsilon_{zz}(t) = \\frac{1}{b(t)} - 1, \\] where \\(\\epsilon_{zz} = 0\\) corresponds to an undeformed ice parcel ( \\(t=0\\) ), and \\(\\epsilon_{zz} = -1\\) is the limit of vanishing parcel height ( \\(t\\rightarrow\\infty\\) ). Consider instead the case where lengthening takes place along the vertical axis and shortening in the horizontal plane. This is achieved by substituting \\(b\\rightarrow b^{-1}\\) which implies \\({\\bf F}={\\bf F}_{\\mathrm{P}}^{-1}\\) and hence is the time reversed behavior of \\({\\bf F}_{\\mathrm{P}}\\) since \\(b^{-1}(t)=b(-t)\\) .","title":"Pure shear"},{"location":"deformation-modes/#example","text":"The above expressions are accessible in specfab as follows import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(4) axis = 2 # axis of shortening (T>0) or lengthening (T<0): 0=x, 1=y, 2=z Tc = 1 # time taken in seconds for parcel to reduce to half (50%) height if T>0, or abs(time) taken for parcel to double in height (200%) if T<0. r = 0 # asymmetry parameter for shortening (if T>0) or lengthening (if T<0) T = Tc/np.log(2) # corresponding e-folding time ugrad = sf.pureshear_ugrad(axis, r, T) # velocity gradient D, W = sf.ugrad_to_D_and_W(ugrad) # strain-rate and spin tensor t = 1 # some specific time of interest b = sf.pureshear_b(T, t) # scaling parameter at time t F = sf.pureshear_F(axis, r, T, t) # deformation gradient tensor at time t eps = sf.F_to_strain(F) # strain tensor at time t","title":"Example"},{"location":"deformation-modes/#simple-shear","text":"Simple shear strain may be characterized by the shear angle \\(\\gamma\\) of the resulting rhombus. In the case of vertical shear in the \\(x\\) \u2014 \\(z\\) plane, the deformation gradient tensor is given by \\[ {\\bf F}_{\\mathrm{S}} = \\begin{bmatrix} 1 & 0 & \\tan(\\gamma) \\\\ 0 & 1 & 0\\\\ 0& 0& 1 \\end{bmatrix} , \\] and velocity-gradient tensor becomes \\[ \\nabla {\\bf u} = \\frac{\\dot{\\gamma}}{\\cos^2(\\gamma)} \\begin{bmatrix} 0 & 0 & 1\\\\ 0 & 0 & 0\\\\ 0& 0& 0 \\end{bmatrix} . \\] For a constant shear rate, \\(1/T\\) , the shear-angle time dependence is \\begin{align} \\gamma(t) = \\tan^{-1}(t/T) , \\end{align} where \\(T\\) is the characteristic time taken to reach a shear of 1 from an undeformed state. In this case, \\(\\nabla {\\bf u}\\) is constant, too, since \\[ \\frac{\\dot{\\gamma}}{\\cos^2(\\gamma)} = \\frac{1}{T} . \\]","title":"Simple shear"},{"location":"deformation-modes/#example_1","text":"The above expressions are accessible in specfab as follows import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(4) plane = 1 # plane of shear: 0=yz, 1=xz, 2=xy T = 1 # time taken in seconds for parcel to a reach shear strain of 1 (45 deg shear angle) ugrad = sf.simpleshear_ugrad(plane, T) # velocity gradient D, W = sf.ugrad_to_D_and_W(ugrad) # strain-rate and spin tensor t = 1 # some specific time of interest gamma = sf.simpleshear_gamma(T, t) # shear angle at time t F = sf.simpleshear_F(plane, T, t) # deformation gradient tensor at time t eps = sf.F_to_strain(F) # strain tensor at time t","title":"Example"},{"location":"enhancements-strainrate/","text":"Strain-rate enhancements Given an anisotropic rheology \\({\\bf D}({\\bf S})\\) , where \\({\\bf D}\\) and \\({\\bf S}\\) are the strain-rate and deviatoric stress tensors, respectively, the directional strain-rate enhancement factors \\(E_{ij}\\) are defined as the \\(({\\bf e}_i, {\\bf e}_j)\\) -components of \\({\\bf D}\\) relative to that of the rheology in the isotropic limit (isotropic CPO): \\[ E_{ij} = \\frac{ {\\bf e}_i \\cdot {\\bf D}({\\bf S}) \\cdot {\\bf e}_j }{ {\\bf e}_i \\cdot {\\bf D}_{\\mathrm{iso}}({\\bf S}) \\cdot {\\bf e}_j } , \\qquad(1) \\] for a stress state aligned with \\(({\\bf e}_i, {\\bf e}_j)\\) : \\[ {\\bf S}({\\bf e}_i, {\\bf e}_j) = \\tau_0 \\begin{cases} {\\bf I} - 3{\\bf e}_i \\otimes {\\bf e}_i \\;\\;\\quad\\quad\\text{if}\\quad i=j \\\\ {\\bf e}_i \\otimes {\\bf e}_j + {\\bf e}_j \\otimes {\\bf e}_i \\quad\\text{if}\\quad i\\neq j \\\\ \\end{cases} . \\] In this way: \\({E_{11}}\\) is the longitudinal strain-rate enhancement along \\({\\bf e}_{1}\\) when subject to compression along \\({\\bf e}_{1}\\) \\({E_{12}}\\) is the \\({\\bf e}_{1}\\) \u2014 \\({\\bf e}_{2}\\) shear strain-rate enhancement when subject to shear in the \\({\\bf e}_{1}\\) \u2014 \\({\\bf e}_{2}\\) plane and so on. Hard or soft \\(E_{ij}>1\\) implies the material response is softened due to fabric (compared to an isotropic CPO), whereas \\(E_{ij}<1\\) implies hardening . Eigenenhancements Eigenenhancements are defined as the enhancement factors w.r.t. the CPO symmetry axes ( \\({\\bf m}_i\\) ): \\[{\\bf e}_i = {\\bf m}_i .\\] These are the enhancements factors needed to specify the viscous anisotropy in bulk rheologies : Transversely isotropic Orthotropic Grain homogenization Calculating \\(E_{ij}\\) using (1) for a given CPO requires an effective rheology that takes the microstructure into account. In the simplest case, polycrystals may be regarded as an ensemble of interactionless grains (monocrystals), subject to either a homogeneous stress field over the polycrystal scale: \\[ {\\bf S}' = {\\bf S} , \\qquad\\qquad \\text{(Sachs's hypothesis)} \\] or a homogeneous stain-rate field: \\[ {\\bf D}' = {\\bf D} , \\qquad\\qquad \\text{(Taylor's hypothesis)} \\] where \\({\\bf S}'\\) and \\({\\bf D}'\\) are the microscopic (grain-scale) stress and strain-rate tensors, respectively. The effective rheology can then be approximated as the ensemble-averaged monocrystal rheology for either case: \\[ {\\bf D}^{\\mathrm{Sachs}} = \\langle {\\bf D}'({\\bf S}') \\rangle = \\langle {\\bf D}'({\\bf S}) \\rangle , \\qquad\\qquad \\text{(Sachs homogenization)} \\] \\[ \\qquad {\\bf D}^{\\mathrm{Taylor}} = \\langle {\\bf S}'({\\bf D}') \\rangle^{-1} = \\langle {\\bf S}'({\\bf D}) \\rangle^{-1} , \\qquad \\text{(Taylor homogenization)} \\] where \\(\\langle \\cdot \\rangle^{-1}\\) inverts the tensorial relationship. If a linear combination of the two homogenizations is considered, equation (1) can be written as \\[ E_{ij} = (1-\\alpha) \\frac{{\\bf e}_i \\cdot {\\bf D}^{\\mathrm{Sachs}}({\\bf S}) \\cdot {\\bf e}_j} {{\\bf e}_i \\cdot {\\bf D}^{\\mathrm{Sachs}}_{\\mathrm{iso}}({\\bf S}) \\cdot {\\bf e}_j } + {\\alpha} \\frac{ {\\bf e}_i \\cdot {\\bf D}^{\\mathrm{Taylor}}({\\bf S}) \\cdot {\\bf e}_j } { {\\bf e}_i \\cdot {\\bf D}^{\\mathrm{Taylor}}_{\\mathrm{iso}}({\\bf S}) \\cdot {\\bf e}_j } , \\] or simply \\[ E_{ij} = (1-\\alpha)E_{ij}^{\\mathrm{Sachs}} + {\\alpha}E_{ij}^{\\mathrm{Taylor}} , \\] where \\(\\alpha\\) is a free parameter. Grain parameters The grain viscous parameters used for homogenization should be understood as the effective values needed to reproduce deformation experiments on polycrystals; they are not the values derived from experiments on single crystals. Transversely isotropic grains Monocrystal Polycrystal If grains are approximately transversely isotropic, the grain rheology can be modelled using the transversely isotropic power-law rheology . This requires specifying the grain eigenenhancements \\(E_{mm}'\\) and \\(E_{mt}'\\) , the power-law exponent \\(n'\\) , and the Taylor\u2014Sachs weight \\(\\alpha\\) . Example for glacier ice For glacier ice, we follow the literature and rename \\[ {\\bf c} = {\\bf m}^\\prime, \\\\ {\\bf a} = {\\bf t}^\\prime. \\] The below code example shows how to calculate \\(E_{ij}\\) given a4 (or nlm ) assuming the grain parameters proposed by Rathmann and Lilien (2021) : import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(8) ### Synthetic unidirectional CPO (all c-axes aligned in z-direction) m = np.array([0,0,1]) a4 = np.einsum('i,j,k,l', m,m,m,m) # 4-times repeated outer product of m nlm = np.zeros((nlm_len), dtype=np.complex64) nlm[:sf.L4len] = sf.a4_to_nlm(a4) # derive corresponding expansion coefficients ### Basis for enhancement factor calculations (e1,e2,e3, eigvals) = sf.frame(nlm, 'e') # enhancement factors are w.r.t. a^(2) basis (i.e. eigenenhancements) #(e1,e2,e3) = np.eye(3) # enhancement factors are w.r.t. Cartesian basis (x,y,z) ### Transversely isotropic monocrystal parameters for ice (Rathmann & Lilien, 2021) n_grain = 1 # power-law exponent: n=1 => linear grain rheology, nonlinear (n>1) is unsupported. Eij_grain = (1, 1e3) # grain eigenenhancements (Ecc,Eca) for compression along c-axis (Ecc) and for shear parallel to basal plane (Eca) alpha = 0.0125 # Taylor--Sachs weight ### Calculate enhancement factors w.r.t. (e1,e2,e3) Eij = sf.Eij_tranisotropic(nlm, e1,e2,e3, Eij_grain,alpha,n_grain) # Eij=(E11,E22,E33,E23,E13,E12) Choosing grain parameters for glacier ice The grain parameters proposed by Rathmann and Lilien (2021) assume a linear viscous ( \\(n'=1\\) ) response and promote the activation of basal glide by making that slip system soft compared to other systems: \\(E_{ca}' > 1\\) , whereas \\(E_{cc}'=1\\) . This reduces the problem to that of picking \\(E_{ca}'\\) and \\(\\alpha\\) , which Rathmann and Lilien (2021) chose such that deformation tests on unidirectional CPOs (perfect single maximum) are approximately reproduced: \\(E_{mt}=10\\) while \\(E_{mt}/E_{pq} \\sim 10^4\\) , where \\(p,q\\) denote directions at \\(45^\\circ\\) to \\({\\bf m}\\) . The effect of choosing alternative \\(E_{ca}'\\) and \\(\\alpha\\) (left plot) on eigenenhancements for different CPO states (right plot) is here shown for combinations of \\(E_{ca}'\\) and \\(\\alpha\\) that fulfill \\(E_{mt}=10\\) for a unidirectional CPO: Clearly, there is a tradeoff between how shear enhanced ( \\(E_{mt}\\) ) and how hard for axial compression ( \\(E_{mm}\\) ) the model allows a unidirectional CPO to be. Evolving CPO The below animation shows the directional enhancement factors for a CPO evolving under uniaxial compression along \\({\\hat {\\bf z}}\\) when subject to lattice rotation . Enhancement factors are calculated w.r.t. the spherical coordinate basis vectors \\(({\\bf e}_1, {\\bf e}_2, {\\bf e}_3) = ({\\hat{\\bf r}},{\\hat{\\boldsymbol \\theta}},{\\hat{\\boldsymbol \\phi}})\\) . Orthotropic grains Monocrystal Polycrystal If grains are approximately orthotropic, the grain rheology can be modelled using the orthotropic power-law rheology . This requires specifying the grain eigenenhancements \\(E_{ij}'\\) , the power-law exponent \\(n'\\) , and the Taylor\u2014Sachs weight \\(\\alpha\\) . Example for olivine Not yet available.","title":"Strain-rate enhancements"},{"location":"enhancements-strainrate/#strain-rate-enhancements","text":"Given an anisotropic rheology \\({\\bf D}({\\bf S})\\) , where \\({\\bf D}\\) and \\({\\bf S}\\) are the strain-rate and deviatoric stress tensors, respectively, the directional strain-rate enhancement factors \\(E_{ij}\\) are defined as the \\(({\\bf e}_i, {\\bf e}_j)\\) -components of \\({\\bf D}\\) relative to that of the rheology in the isotropic limit (isotropic CPO): \\[ E_{ij} = \\frac{ {\\bf e}_i \\cdot {\\bf D}({\\bf S}) \\cdot {\\bf e}_j }{ {\\bf e}_i \\cdot {\\bf D}_{\\mathrm{iso}}({\\bf S}) \\cdot {\\bf e}_j } , \\qquad(1) \\] for a stress state aligned with \\(({\\bf e}_i, {\\bf e}_j)\\) : \\[ {\\bf S}({\\bf e}_i, {\\bf e}_j) = \\tau_0 \\begin{cases} {\\bf I} - 3{\\bf e}_i \\otimes {\\bf e}_i \\;\\;\\quad\\quad\\text{if}\\quad i=j \\\\ {\\bf e}_i \\otimes {\\bf e}_j + {\\bf e}_j \\otimes {\\bf e}_i \\quad\\text{if}\\quad i\\neq j \\\\ \\end{cases} . \\] In this way: \\({E_{11}}\\) is the longitudinal strain-rate enhancement along \\({\\bf e}_{1}\\) when subject to compression along \\({\\bf e}_{1}\\) \\({E_{12}}\\) is the \\({\\bf e}_{1}\\) \u2014 \\({\\bf e}_{2}\\) shear strain-rate enhancement when subject to shear in the \\({\\bf e}_{1}\\) \u2014 \\({\\bf e}_{2}\\) plane and so on. Hard or soft \\(E_{ij}>1\\) implies the material response is softened due to fabric (compared to an isotropic CPO), whereas \\(E_{ij}<1\\) implies hardening .","title":"Strain-rate enhancements"},{"location":"enhancements-strainrate/#eigenenhancements","text":"Eigenenhancements are defined as the enhancement factors w.r.t. the CPO symmetry axes ( \\({\\bf m}_i\\) ): \\[{\\bf e}_i = {\\bf m}_i .\\] These are the enhancements factors needed to specify the viscous anisotropy in bulk rheologies : Transversely isotropic Orthotropic","title":"Eigenenhancements"},{"location":"enhancements-strainrate/#grain-homogenization","text":"Calculating \\(E_{ij}\\) using (1) for a given CPO requires an effective rheology that takes the microstructure into account. In the simplest case, polycrystals may be regarded as an ensemble of interactionless grains (monocrystals), subject to either a homogeneous stress field over the polycrystal scale: \\[ {\\bf S}' = {\\bf S} , \\qquad\\qquad \\text{(Sachs's hypothesis)} \\] or a homogeneous stain-rate field: \\[ {\\bf D}' = {\\bf D} , \\qquad\\qquad \\text{(Taylor's hypothesis)} \\] where \\({\\bf S}'\\) and \\({\\bf D}'\\) are the microscopic (grain-scale) stress and strain-rate tensors, respectively. The effective rheology can then be approximated as the ensemble-averaged monocrystal rheology for either case: \\[ {\\bf D}^{\\mathrm{Sachs}} = \\langle {\\bf D}'({\\bf S}') \\rangle = \\langle {\\bf D}'({\\bf S}) \\rangle , \\qquad\\qquad \\text{(Sachs homogenization)} \\] \\[ \\qquad {\\bf D}^{\\mathrm{Taylor}} = \\langle {\\bf S}'({\\bf D}') \\rangle^{-1} = \\langle {\\bf S}'({\\bf D}) \\rangle^{-1} , \\qquad \\text{(Taylor homogenization)} \\] where \\(\\langle \\cdot \\rangle^{-1}\\) inverts the tensorial relationship. If a linear combination of the two homogenizations is considered, equation (1) can be written as \\[ E_{ij} = (1-\\alpha) \\frac{{\\bf e}_i \\cdot {\\bf D}^{\\mathrm{Sachs}}({\\bf S}) \\cdot {\\bf e}_j} {{\\bf e}_i \\cdot {\\bf D}^{\\mathrm{Sachs}}_{\\mathrm{iso}}({\\bf S}) \\cdot {\\bf e}_j } + {\\alpha} \\frac{ {\\bf e}_i \\cdot {\\bf D}^{\\mathrm{Taylor}}({\\bf S}) \\cdot {\\bf e}_j } { {\\bf e}_i \\cdot {\\bf D}^{\\mathrm{Taylor}}_{\\mathrm{iso}}({\\bf S}) \\cdot {\\bf e}_j } , \\] or simply \\[ E_{ij} = (1-\\alpha)E_{ij}^{\\mathrm{Sachs}} + {\\alpha}E_{ij}^{\\mathrm{Taylor}} , \\] where \\(\\alpha\\) is a free parameter. Grain parameters The grain viscous parameters used for homogenization should be understood as the effective values needed to reproduce deformation experiments on polycrystals; they are not the values derived from experiments on single crystals.","title":"Grain homogenization"},{"location":"enhancements-strainrate/#transversely-isotropic-grains","text":"Monocrystal Polycrystal If grains are approximately transversely isotropic, the grain rheology can be modelled using the transversely isotropic power-law rheology . This requires specifying the grain eigenenhancements \\(E_{mm}'\\) and \\(E_{mt}'\\) , the power-law exponent \\(n'\\) , and the Taylor\u2014Sachs weight \\(\\alpha\\) .","title":"Transversely isotropic grains"},{"location":"enhancements-strainrate/#example-for-glacier-ice","text":"For glacier ice, we follow the literature and rename \\[ {\\bf c} = {\\bf m}^\\prime, \\\\ {\\bf a} = {\\bf t}^\\prime. \\] The below code example shows how to calculate \\(E_{ij}\\) given a4 (or nlm ) assuming the grain parameters proposed by Rathmann and Lilien (2021) : import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(8) ### Synthetic unidirectional CPO (all c-axes aligned in z-direction) m = np.array([0,0,1]) a4 = np.einsum('i,j,k,l', m,m,m,m) # 4-times repeated outer product of m nlm = np.zeros((nlm_len), dtype=np.complex64) nlm[:sf.L4len] = sf.a4_to_nlm(a4) # derive corresponding expansion coefficients ### Basis for enhancement factor calculations (e1,e2,e3, eigvals) = sf.frame(nlm, 'e') # enhancement factors are w.r.t. a^(2) basis (i.e. eigenenhancements) #(e1,e2,e3) = np.eye(3) # enhancement factors are w.r.t. Cartesian basis (x,y,z) ### Transversely isotropic monocrystal parameters for ice (Rathmann & Lilien, 2021) n_grain = 1 # power-law exponent: n=1 => linear grain rheology, nonlinear (n>1) is unsupported. Eij_grain = (1, 1e3) # grain eigenenhancements (Ecc,Eca) for compression along c-axis (Ecc) and for shear parallel to basal plane (Eca) alpha = 0.0125 # Taylor--Sachs weight ### Calculate enhancement factors w.r.t. (e1,e2,e3) Eij = sf.Eij_tranisotropic(nlm, e1,e2,e3, Eij_grain,alpha,n_grain) # Eij=(E11,E22,E33,E23,E13,E12) Choosing grain parameters for glacier ice The grain parameters proposed by Rathmann and Lilien (2021) assume a linear viscous ( \\(n'=1\\) ) response and promote the activation of basal glide by making that slip system soft compared to other systems: \\(E_{ca}' > 1\\) , whereas \\(E_{cc}'=1\\) . This reduces the problem to that of picking \\(E_{ca}'\\) and \\(\\alpha\\) , which Rathmann and Lilien (2021) chose such that deformation tests on unidirectional CPOs (perfect single maximum) are approximately reproduced: \\(E_{mt}=10\\) while \\(E_{mt}/E_{pq} \\sim 10^4\\) , where \\(p,q\\) denote directions at \\(45^\\circ\\) to \\({\\bf m}\\) . The effect of choosing alternative \\(E_{ca}'\\) and \\(\\alpha\\) (left plot) on eigenenhancements for different CPO states (right plot) is here shown for combinations of \\(E_{ca}'\\) and \\(\\alpha\\) that fulfill \\(E_{mt}=10\\) for a unidirectional CPO: Clearly, there is a tradeoff between how shear enhanced ( \\(E_{mt}\\) ) and how hard for axial compression ( \\(E_{mm}\\) ) the model allows a unidirectional CPO to be. Evolving CPO The below animation shows the directional enhancement factors for a CPO evolving under uniaxial compression along \\({\\hat {\\bf z}}\\) when subject to lattice rotation . Enhancement factors are calculated w.r.t. the spherical coordinate basis vectors \\(({\\bf e}_1, {\\bf e}_2, {\\bf e}_3) = ({\\hat{\\bf r}},{\\hat{\\boldsymbol \\theta}},{\\hat{\\boldsymbol \\phi}})\\) .","title":"Example for glacier ice"},{"location":"enhancements-strainrate/#orthotropic-grains","text":"Monocrystal Polycrystal If grains are approximately orthotropic, the grain rheology can be modelled using the orthotropic power-law rheology . This requires specifying the grain eigenenhancements \\(E_{ij}'\\) , the power-law exponent \\(n'\\) , and the Taylor\u2014Sachs weight \\(\\alpha\\) .","title":"Orthotropic grains"},{"location":"enhancements-strainrate/#example-for-olivine","text":"Not yet available.","title":"Example for olivine"},{"location":"plotting/","text":"Plotting CPO The orientation distribution function (ODF; normalized expansion series) can be plotted as follows: import numpy as np import matplotlib.pyplot as plt from specfabpy import specfab as sf from specfabpy import plotting as sfplt lm, nlm_len = sf.init(6) ### CPO to be plotted a2 = np.diag([0,0,1]) # CPO characterized by a^(2) nlm = sf.a2_to_nlm(a2) # vector of expansion coefficients ### Setup axes and projection geo, prj = sfplt.getprojection(rotation=45, inclination=45) fig = plt.figure(figsize=(2,2)) ax = plt.subplot(111, projection=prj) ax.set_global() # ensure entire S^2 is shown ### Plot lvlset = 'iso-up' # default level set: lowest tick/level is the value of an isotropic distribution lvlset = (np.linspace(0,0.8,9), lambda x,p:'%.1f'%x) # custom level set: (list of levels, how to format colorbar tick labels) sfplt.plotODF(nlm, lm, ax, cmap='Greys', lvlset=lvlset) # plot distribution (see src/specfabpy/plotting.py for API) sfplt.plotcoordaxes(ax, geo, color=sfplt.c_dred) # plot coordinate axes (see src/specfabpy/plotting.py for API) plt.savefig('ODF-plot.png', dpi=175, pad_inches=0.1, bbox_inches='tight') Parcel deformation Given a deformation gradient \\({\\bf F}\\) , the effect on an undeformed parcel can be plotting following: import numpy as np import matplotlib.pyplot as plt from specfabpy import specfab as sf from specfabpy import plotting as sfplt lm, nlm_len = sf.init(6) ### Determine deformation gradient F # Pure shear axis = 2 # axis of compression/extension (0=x, 1=y, 2=z) r = 0 # deformation asymmetry T_ps = 1 # e-folding time scale t_ps = 1 # time at which deformed parcel is sought F_ps = sf.pureshear_F(axis, r, T_ps, t_ps) # deformation gradient tensor # Simple shear plane = 1 # shear plane (0=yz, 1=xz, 2=xy) T_ss = 1 # characteristic time taken to reach shear strain 45 deg. t_ss = 1 # time at which deformed parcel is sought F_ss = sf.simpleshear_F(plane, T_ss, t_ss) # deformation gradient tensor ### Plot fig = plt.figure(figsize=(6,6)) ax1 = plt.subplot(121, projection='3d') ax2 = plt.subplot(122, projection='3d') sfplt.plotparcel(ax1, F_ps, azim=35, axscale=1.7, axislabels=True, drawinit=True) sfplt.plotparcel(ax2, F_ss, azim=35, axscale=1.7, axislabels=True, drawinit=True) ax1.set_title(r'$\\epsilon_{zz}=%.2f$'%(sf.F_to_strain(F_ps)[2,2])) ax2.set_title(r'$\\gamma=%.0f$ deg.'%(np.rad2deg(sf.simpleshear_gamma(T_ss, t_ss)))) plt.savefig('deformed-parcel.png', dpi=175, pad_inches=0.1, bbox_inches='tight')","title":"Plotting"},{"location":"plotting/#plotting","text":"","title":"Plotting"},{"location":"plotting/#cpo","text":"The orientation distribution function (ODF; normalized expansion series) can be plotted as follows: import numpy as np import matplotlib.pyplot as plt from specfabpy import specfab as sf from specfabpy import plotting as sfplt lm, nlm_len = sf.init(6) ### CPO to be plotted a2 = np.diag([0,0,1]) # CPO characterized by a^(2) nlm = sf.a2_to_nlm(a2) # vector of expansion coefficients ### Setup axes and projection geo, prj = sfplt.getprojection(rotation=45, inclination=45) fig = plt.figure(figsize=(2,2)) ax = plt.subplot(111, projection=prj) ax.set_global() # ensure entire S^2 is shown ### Plot lvlset = 'iso-up' # default level set: lowest tick/level is the value of an isotropic distribution lvlset = (np.linspace(0,0.8,9), lambda x,p:'%.1f'%x) # custom level set: (list of levels, how to format colorbar tick labels) sfplt.plotODF(nlm, lm, ax, cmap='Greys', lvlset=lvlset) # plot distribution (see src/specfabpy/plotting.py for API) sfplt.plotcoordaxes(ax, geo, color=sfplt.c_dred) # plot coordinate axes (see src/specfabpy/plotting.py for API) plt.savefig('ODF-plot.png', dpi=175, pad_inches=0.1, bbox_inches='tight')","title":"CPO"},{"location":"plotting/#parcel-deformation","text":"Given a deformation gradient \\({\\bf F}\\) , the effect on an undeformed parcel can be plotting following: import numpy as np import matplotlib.pyplot as plt from specfabpy import specfab as sf from specfabpy import plotting as sfplt lm, nlm_len = sf.init(6) ### Determine deformation gradient F # Pure shear axis = 2 # axis of compression/extension (0=x, 1=y, 2=z) r = 0 # deformation asymmetry T_ps = 1 # e-folding time scale t_ps = 1 # time at which deformed parcel is sought F_ps = sf.pureshear_F(axis, r, T_ps, t_ps) # deformation gradient tensor # Simple shear plane = 1 # shear plane (0=yz, 1=xz, 2=xy) T_ss = 1 # characteristic time taken to reach shear strain 45 deg. t_ss = 1 # time at which deformed parcel is sought F_ss = sf.simpleshear_F(plane, T_ss, t_ss) # deformation gradient tensor ### Plot fig = plt.figure(figsize=(6,6)) ax1 = plt.subplot(121, projection='3d') ax2 = plt.subplot(122, projection='3d') sfplt.plotparcel(ax1, F_ps, azim=35, axscale=1.7, axislabels=True, drawinit=True) sfplt.plotparcel(ax2, F_ss, azim=35, axscale=1.7, axislabels=True, drawinit=True) ax1.set_title(r'$\\epsilon_{zz}=%.2f$'%(sf.F_to_strain(F_ps)[2,2])) ax2.set_title(r'$\\gamma=%.0f$ deg.'%(np.rad2deg(sf.simpleshear_gamma(T_ss, t_ss)))) plt.savefig('deformed-parcel.png', dpi=175, pad_inches=0.1, bbox_inches='tight')","title":"Parcel deformation"},{"location":"radar-derived-PP/","text":"Radar-derived physical properties of glacier ice Introduction The dielectric permittivity tensor of a single ice crystal is approximately transversely isotropic w.r.t. the crystal \\(c\\) -axis: $$ \\epsilon_{ij}' = (2\\epsilon_{\\perp}' + \\epsilon_{\\parallel}') \\frac{\\delta_{ij}}{3} + (\\epsilon_{\\parallel}'-\\epsilon_{\\perp}') \\left(c_i c_j - \\frac{\\delta_{ij}}{3} \\right), $$ where \\(\\epsilon_{\\parallel}'\\) and \\(\\epsilon_{\\perp}'\\) are the components parallel and perpendicular to the \\(c\\) -axis, respectively, which depend on ice temperature and EM-wave frequency ( Fujita et al., 2000 ). For wave lengths much longer than the average grain size, the bulk permittivity tensor of polycrystalline ice may be approximated as the grain-average permittivity tensor, \\(\\epsilon_{ij} \\simeq \\langle \\epsilon_{ij}' \\rangle\\) , constructed by averaging over all grain orientations (over the CPO) assuming grain sizes are uncorrelated with orientation: \\[ \\langle \\epsilon_{ij}' \\rangle = (2\\epsilon_{\\perp}' + \\epsilon_{\\parallel}') \\frac{\\delta_{ij}}{3} + (\\epsilon_{\\parallel}'-\\epsilon_{\\perp}') \\left(\\langle c_i c_j \\rangle - \\frac{\\delta_{ij}}{3} \\right) , \\] where \\(\\langle c_i c_j \\rangle\\) is the second-order structure tensor , defined as \\[ \\langle c_i c_j \\rangle = \\frac{1}{N}\\sum_{k=1}^N { c_i^{(k)} c_j^{(k)} }. \\] Thus, because the bulk permittivity tensor \\(\\epsilon_{ij}\\) can be inferred from EM-wave speeds and radar return-power anomalies, so can \\(\\langle c_i c_j \\rangle\\) . Radar measurements \\(\\rightarrow\\) CPO A useful approximation over large parts of ice sheets is that \\(\\langle c_i c_j \\rangle\\) has a vertical eigenvector, in which case the Cartesian components are \\[ \\langle c_i c_j \\rangle = \\left[\\begin{matrix} a_{xx} & a_{xy} & 0\\\\ a_{xy} & a_{yy} & 0\\\\ 0 & 0 & a_{zz} \\end{matrix}\\right] . \\] Let us consider the usual case where the difference in horizontal eigenvalues of \\(\\langle c_i c_j \\rangle\\) , \\[ \\Delta \\lambda = \\lambda_2 - \\lambda_1, \\] can be inferred from ice-penetrating radar, where \\({\\bf m}_1\\) and \\({\\bf m}_2\\) are the corresponding horizontal eigenvectors and eigenvalues are sorted such that \\(\\lambda_1 \\leq \\lambda_2\\) . It follows that the structure tensor, posed in its eigenframe ( \\({\\bf m}_1, {\\bf m}_2, {\\bf z}\\) ), is \\[ \\langle c_i c_j \\rangle = \\left[\\begin{matrix} \\lambda_1 & 0 & 0 \\\\ 0 & \\lambda_1 + \\Delta\\lambda & 0 \\\\ 0 & 0 & 1 - \\Delta \\lambda - 2\\lambda_1 \\end{matrix}\\right] , \\] where the identity \\(\\operatorname{tr}(\\langle c_i c_j \\rangle) = 1\\) was used. Gerber's approximation Since \\(\\lambda_1\\) is unknown, the problem can be closed by making different assumptions about \\(\\lambda_1\\) given the local/upstream flow regime, such as proposed by Gerber et al. (2023) . Suppose \\(\\Delta\\lambda\\) is measured in region where \\(c\\) -axes are, to a good approximation, suspected to be distributed on the \\({\\bf m}_2\\) \u2014 \\({\\bf z}\\) plane because the smallest eigenvalue is vanishing, \\(\\lambda_1 \\rightarrow 0\\) . In this case, \\(\\Delta \\lambda = 0\\) represents a perfect single-maximum along \\({\\bf z}\\) , \\(\\Delta \\lambda = 0.5\\) a perfect girdle in the \\({\\bf m}_2\\) \u2014 \\({\\bf z}\\) plane, and \\(\\Delta \\lambda = 1\\) a perfect single-maximum along \\({\\bf m}_2\\) , respectively: CPO \\(\\rightarrow\\) Enhancement factors If \\(\\langle c_i c_j \\rangle\\) can be inferred from radar sounding following the above method, so can the bulk strain-rate enhancement factors, \\(E_{ij}\\) , in the same eigenframe (i.e. eigenenhancements ). The eigenenhancements depend, however, also on the fourth-order structure tensor, \\(\\langle c_i c_j c_k c_l \\rangle\\) , but the bulk permittivity \\(\\epsilon_{ij}\\) is insensitive to \\(\\langle c_i c_j c_k c_l \\rangle\\) . To overcome this, a simple empirical correlation is adopted that allows determining \\(\\langle c_i c_j c_k c_l \\rangle\\) given \\(\\langle c_i c_j\\rangle\\) if the CPO is approximately rotationally symmetric. Correlation between \\(\\langle c_i c_j c_k c_l \\rangle\\) and \\(\\langle c_i c_j\\rangle\\) If the CPO symmetry axis is rotated into the vertical direction, \\(\\langle c_i c_j\\rangle\\) depends only on the normalized spectral component \\(\\hat{n}_2^0 = n_2^0/n_0^0:\\) \\[ \\langle c_i c_j\\rangle = \\frac{\\delta_{ij}}{3} + \\frac{2\\sqrt{5}}{15} \\hat{n}_2^0 \\left[\\begin{matrix} -1/2 & 0 & 0 \\\\ 0 & -1/2 & 0 \\\\ 0 & 0 & 1 \\end{matrix}\\right] , \\] and \\(\\langle c_i c_j c_k c_l \\rangle\\) only on \\(\\hat{n}_2^0\\) and \\(\\hat{n}_4^0 = n_4^0/n_0^0\\) (not shown). The figure below shows the empirical correlation between these two components based on ice-core samples. Thus, if \\(\\hat{n}_2^0\\) is extracted from \\(\\langle c_i c_j\\rangle\\) in this frame, \\(\\hat{n}_4^0\\) can be derived and hence \\(\\langle c_i c_j c_k c_l \\rangle\\) constructed. To pose the CPO in the original, unrotated eigenframe ( \\({\\bf m}_1, {\\bf m}_2, {\\bf z}\\) ), the resulting expansion series is finally rotated back, allowing eigenenhancements to easily be calculated using specfab. Code example The following code demonstrates how to take each step with specfab: import numpy as np from scipy.spatial.transform import Rotation from specfabpy import specfab as sf lm, nlm_len = sf.init(4) # L=4 is sufficient here ### Determine from radar-derived Delta lambda l1 = 0 # lambda_1 = 0 (Gerber's approximation) dl = 0.5 # Delta lambda = lambda_2 - lambda_1 a2 = np.diag([l1, l1+dl, 1-dl-2*l1]) # second-order structure tensor, , in eigenframe m1, m2, z = np.array([1,0,0]), np.array([0,1,0]), np.array([0,0,1]) # eigenvectors ### Rotate into a rotationally-symmetric frame about z Rm1 = Rotation.from_rotvec(np.pi/2 * m1).as_matrix() # Rotate 90 deg about m1 eigenvector Rm2 = Rotation.from_rotvec(np.pi/2 * m2).as_matrix() # Rotate 90 deg about m2 eigenvector if dl < 0.4: a2_vs = a2 # Already in rotationally-symmetric frame about z if 0.4 <= dl <= 0.6: a2_vs = np.matmul(Rm2,np.matmul(a2,Rm2.T)) # Rotate vertical (m2--z) girdle into horizontal (m1--m2) girdle if dl > 0.6: a2_vs = np.matmul(Rm1,np.matmul(a2,Rm1.T)) # Rotate horizontal (m2) single-maximum into vertical (z) single-maximum ### Determine \\hat{n}_4^0 (= n_4^0/n_0^0) from \\hat{n}_2^0 (= n_2^0/n_0^0) in rotationally-symmetric frame about z nhat20 = (a2_vs[2,2]- 1/3)/(2/15*np.sqrt(5)) # azz -> nhat20 nhat40 = sf.nhat40_empcorr_ice(nhat20)[0] ### Construct nlm (spectral CPO state vector) in rotationally-symmetric frame about z nlm_vs = np.zeros(nlm_len, dtype=np.complex128) n00 = 1/np.sqrt(4*np.pi) # only grain-number normalized distribution is known, so must integrate to 1 over S^2. nlm_vs[0] = n00 nlm_vs[3] = nhat20*n00 nlm_vs[10] = nhat40*n00 ### Rotate spectral CPO state back to origional (m1,m2,z) eigenframe if dl < 0.4: nlm = nlm_vs # Already in vertical symmetric frame if 0.4 <= dl <= 0.6: nlm = sf.rotate_nlm(nlm_vs, -np.pi/2, 0) # Rotate horizontal (m1--m2) girdle back into vertical (m2--z) girdle if dl > 0.6: nlm = sf.rotate_nlm(sf.rotate_nlm(nlm_vs, -np.pi/2, 0), 0 ,-np.pi/2) # Rotate vertical (z) single-maximum back into horizontal (m2) single-maximum ### Calculate eigenenhancements # Transversely isotropic monocrystal parameters for ice (Rathmann & Lilien, 2021) n_grain = 1 # Power-law exponent: n=1 => linear grain rheology, nonlinear (n>1) is unsupported Eij_grain = (1, 1e3) # Grain eigenenhancements (Ecc,Eca) for compression along c-axis (Ecc) and for shear parallel to basal plane (Eca) alpha = 0.0125 # Taylor--Sachs weight # Tuple of eigenenhancements (bulk enhancement factors w.r.t. m1, m2, z) e1, e2, e3 = m1, m2, z Eij = sf.Eij_tranisotropic(nlm, e1,e2,e3, Eij_grain,alpha,n_grain) # (E_{m1,m1},E_{m2,m2},E_{zz},E_{m2,z),E_{m1,z},E_{m1,m2}) # To calculate bulk enhancement factors w.r.t. other axes of deformation/stress, change (e1,e2,e3) accordingly. For reference, the below plots show the different CPOs at each step for \\(\\Delta\\lambda=0.5\\) and \\(\\Delta\\lambda=1\\) . \\(\\Delta\\lambda = 0.5\\) \\(\\Delta\\lambda = 1.0\\)","title":"Radar-derived physical properties"},{"location":"radar-derived-PP/#radar-derived-physical-properties-of-glacier-ice","text":"","title":"Radar-derived physical properties of glacier ice"},{"location":"radar-derived-PP/#introduction","text":"The dielectric permittivity tensor of a single ice crystal is approximately transversely isotropic w.r.t. the crystal \\(c\\) -axis: $$ \\epsilon_{ij}' = (2\\epsilon_{\\perp}' + \\epsilon_{\\parallel}') \\frac{\\delta_{ij}}{3} + (\\epsilon_{\\parallel}'-\\epsilon_{\\perp}') \\left(c_i c_j - \\frac{\\delta_{ij}}{3} \\right), $$ where \\(\\epsilon_{\\parallel}'\\) and \\(\\epsilon_{\\perp}'\\) are the components parallel and perpendicular to the \\(c\\) -axis, respectively, which depend on ice temperature and EM-wave frequency ( Fujita et al., 2000 ). For wave lengths much longer than the average grain size, the bulk permittivity tensor of polycrystalline ice may be approximated as the grain-average permittivity tensor, \\(\\epsilon_{ij} \\simeq \\langle \\epsilon_{ij}' \\rangle\\) , constructed by averaging over all grain orientations (over the CPO) assuming grain sizes are uncorrelated with orientation: \\[ \\langle \\epsilon_{ij}' \\rangle = (2\\epsilon_{\\perp}' + \\epsilon_{\\parallel}') \\frac{\\delta_{ij}}{3} + (\\epsilon_{\\parallel}'-\\epsilon_{\\perp}') \\left(\\langle c_i c_j \\rangle - \\frac{\\delta_{ij}}{3} \\right) , \\] where \\(\\langle c_i c_j \\rangle\\) is the second-order structure tensor , defined as \\[ \\langle c_i c_j \\rangle = \\frac{1}{N}\\sum_{k=1}^N { c_i^{(k)} c_j^{(k)} }. \\] Thus, because the bulk permittivity tensor \\(\\epsilon_{ij}\\) can be inferred from EM-wave speeds and radar return-power anomalies, so can \\(\\langle c_i c_j \\rangle\\) .","title":"Introduction"},{"location":"radar-derived-PP/#radar-measurements-rightarrow-cpo","text":"A useful approximation over large parts of ice sheets is that \\(\\langle c_i c_j \\rangle\\) has a vertical eigenvector, in which case the Cartesian components are \\[ \\langle c_i c_j \\rangle = \\left[\\begin{matrix} a_{xx} & a_{xy} & 0\\\\ a_{xy} & a_{yy} & 0\\\\ 0 & 0 & a_{zz} \\end{matrix}\\right] . \\] Let us consider the usual case where the difference in horizontal eigenvalues of \\(\\langle c_i c_j \\rangle\\) , \\[ \\Delta \\lambda = \\lambda_2 - \\lambda_1, \\] can be inferred from ice-penetrating radar, where \\({\\bf m}_1\\) and \\({\\bf m}_2\\) are the corresponding horizontal eigenvectors and eigenvalues are sorted such that \\(\\lambda_1 \\leq \\lambda_2\\) . It follows that the structure tensor, posed in its eigenframe ( \\({\\bf m}_1, {\\bf m}_2, {\\bf z}\\) ), is \\[ \\langle c_i c_j \\rangle = \\left[\\begin{matrix} \\lambda_1 & 0 & 0 \\\\ 0 & \\lambda_1 + \\Delta\\lambda & 0 \\\\ 0 & 0 & 1 - \\Delta \\lambda - 2\\lambda_1 \\end{matrix}\\right] , \\] where the identity \\(\\operatorname{tr}(\\langle c_i c_j \\rangle) = 1\\) was used.","title":"Radar measurements \\(\\rightarrow\\) CPO"},{"location":"radar-derived-PP/#gerbers-approximation","text":"Since \\(\\lambda_1\\) is unknown, the problem can be closed by making different assumptions about \\(\\lambda_1\\) given the local/upstream flow regime, such as proposed by Gerber et al. (2023) . Suppose \\(\\Delta\\lambda\\) is measured in region where \\(c\\) -axes are, to a good approximation, suspected to be distributed on the \\({\\bf m}_2\\) \u2014 \\({\\bf z}\\) plane because the smallest eigenvalue is vanishing, \\(\\lambda_1 \\rightarrow 0\\) . In this case, \\(\\Delta \\lambda = 0\\) represents a perfect single-maximum along \\({\\bf z}\\) , \\(\\Delta \\lambda = 0.5\\) a perfect girdle in the \\({\\bf m}_2\\) \u2014 \\({\\bf z}\\) plane, and \\(\\Delta \\lambda = 1\\) a perfect single-maximum along \\({\\bf m}_2\\) , respectively:","title":"Gerber's approximation"},{"location":"radar-derived-PP/#cpo-rightarrow-enhancement-factors","text":"If \\(\\langle c_i c_j \\rangle\\) can be inferred from radar sounding following the above method, so can the bulk strain-rate enhancement factors, \\(E_{ij}\\) , in the same eigenframe (i.e. eigenenhancements ). The eigenenhancements depend, however, also on the fourth-order structure tensor, \\(\\langle c_i c_j c_k c_l \\rangle\\) , but the bulk permittivity \\(\\epsilon_{ij}\\) is insensitive to \\(\\langle c_i c_j c_k c_l \\rangle\\) . To overcome this, a simple empirical correlation is adopted that allows determining \\(\\langle c_i c_j c_k c_l \\rangle\\) given \\(\\langle c_i c_j\\rangle\\) if the CPO is approximately rotationally symmetric.","title":"CPO \\(\\rightarrow\\) Enhancement factors"},{"location":"radar-derived-PP/#correlation-between-langle-c_i-c_j-c_k-c_l-rangle-and-langle-c_i-c_jrangle","text":"If the CPO symmetry axis is rotated into the vertical direction, \\(\\langle c_i c_j\\rangle\\) depends only on the normalized spectral component \\(\\hat{n}_2^0 = n_2^0/n_0^0:\\) \\[ \\langle c_i c_j\\rangle = \\frac{\\delta_{ij}}{3} + \\frac{2\\sqrt{5}}{15} \\hat{n}_2^0 \\left[\\begin{matrix} -1/2 & 0 & 0 \\\\ 0 & -1/2 & 0 \\\\ 0 & 0 & 1 \\end{matrix}\\right] , \\] and \\(\\langle c_i c_j c_k c_l \\rangle\\) only on \\(\\hat{n}_2^0\\) and \\(\\hat{n}_4^0 = n_4^0/n_0^0\\) (not shown). The figure below shows the empirical correlation between these two components based on ice-core samples. Thus, if \\(\\hat{n}_2^0\\) is extracted from \\(\\langle c_i c_j\\rangle\\) in this frame, \\(\\hat{n}_4^0\\) can be derived and hence \\(\\langle c_i c_j c_k c_l \\rangle\\) constructed. To pose the CPO in the original, unrotated eigenframe ( \\({\\bf m}_1, {\\bf m}_2, {\\bf z}\\) ), the resulting expansion series is finally rotated back, allowing eigenenhancements to easily be calculated using specfab.","title":"Correlation between \\(\\langle c_i c_j c_k c_l \\rangle\\) and \\(\\langle c_i c_j\\rangle\\)"},{"location":"radar-derived-PP/#code-example","text":"The following code demonstrates how to take each step with specfab: import numpy as np from scipy.spatial.transform import Rotation from specfabpy import specfab as sf lm, nlm_len = sf.init(4) # L=4 is sufficient here ### Determine from radar-derived Delta lambda l1 = 0 # lambda_1 = 0 (Gerber's approximation) dl = 0.5 # Delta lambda = lambda_2 - lambda_1 a2 = np.diag([l1, l1+dl, 1-dl-2*l1]) # second-order structure tensor, , in eigenframe m1, m2, z = np.array([1,0,0]), np.array([0,1,0]), np.array([0,0,1]) # eigenvectors ### Rotate into a rotationally-symmetric frame about z Rm1 = Rotation.from_rotvec(np.pi/2 * m1).as_matrix() # Rotate 90 deg about m1 eigenvector Rm2 = Rotation.from_rotvec(np.pi/2 * m2).as_matrix() # Rotate 90 deg about m2 eigenvector if dl < 0.4: a2_vs = a2 # Already in rotationally-symmetric frame about z if 0.4 <= dl <= 0.6: a2_vs = np.matmul(Rm2,np.matmul(a2,Rm2.T)) # Rotate vertical (m2--z) girdle into horizontal (m1--m2) girdle if dl > 0.6: a2_vs = np.matmul(Rm1,np.matmul(a2,Rm1.T)) # Rotate horizontal (m2) single-maximum into vertical (z) single-maximum ### Determine \\hat{n}_4^0 (= n_4^0/n_0^0) from \\hat{n}_2^0 (= n_2^0/n_0^0) in rotationally-symmetric frame about z nhat20 = (a2_vs[2,2]- 1/3)/(2/15*np.sqrt(5)) # azz -> nhat20 nhat40 = sf.nhat40_empcorr_ice(nhat20)[0] ### Construct nlm (spectral CPO state vector) in rotationally-symmetric frame about z nlm_vs = np.zeros(nlm_len, dtype=np.complex128) n00 = 1/np.sqrt(4*np.pi) # only grain-number normalized distribution is known, so must integrate to 1 over S^2. nlm_vs[0] = n00 nlm_vs[3] = nhat20*n00 nlm_vs[10] = nhat40*n00 ### Rotate spectral CPO state back to origional (m1,m2,z) eigenframe if dl < 0.4: nlm = nlm_vs # Already in vertical symmetric frame if 0.4 <= dl <= 0.6: nlm = sf.rotate_nlm(nlm_vs, -np.pi/2, 0) # Rotate horizontal (m1--m2) girdle back into vertical (m2--z) girdle if dl > 0.6: nlm = sf.rotate_nlm(sf.rotate_nlm(nlm_vs, -np.pi/2, 0), 0 ,-np.pi/2) # Rotate vertical (z) single-maximum back into horizontal (m2) single-maximum ### Calculate eigenenhancements # Transversely isotropic monocrystal parameters for ice (Rathmann & Lilien, 2021) n_grain = 1 # Power-law exponent: n=1 => linear grain rheology, nonlinear (n>1) is unsupported Eij_grain = (1, 1e3) # Grain eigenenhancements (Ecc,Eca) for compression along c-axis (Ecc) and for shear parallel to basal plane (Eca) alpha = 0.0125 # Taylor--Sachs weight # Tuple of eigenenhancements (bulk enhancement factors w.r.t. m1, m2, z) e1, e2, e3 = m1, m2, z Eij = sf.Eij_tranisotropic(nlm, e1,e2,e3, Eij_grain,alpha,n_grain) # (E_{m1,m1},E_{m2,m2},E_{zz},E_{m2,z),E_{m1,z},E_{m1,m2}) # To calculate bulk enhancement factors w.r.t. other axes of deformation/stress, change (e1,e2,e3) accordingly. For reference, the below plots show the different CPOs at each step for \\(\\Delta\\lambda=0.5\\) and \\(\\Delta\\lambda=1\\) . \\(\\Delta\\lambda = 0.5\\) \\(\\Delta\\lambda = 1.0\\)","title":"Code example"},{"location":"wavepropagation-elastic/","text":"Elastic wave propagation Problem We seek plane wave solutions of the Cauchy-Navier equation of motion \\[ \\nabla\\cdot {\\boldsymbol \\sigma} = \\rho \\frac{\\partial^2 {\\bf d}}{\\partial t^2}, \\] where \\({\\boldsymbol \\sigma}\\) is the bulk stress tensor, \\({\\bf d}\\) is the displacement field, and \\(\\rho\\) the mass density. Substituting \\({\\bf d}\\) for a plane wave solution, \\({\\bf d} = {\\bf d}_0 \\exp[i({\\bf k}\\cdot {\\bf x} - \\omega t)]\\) , the problem reduces to \\[ (k^2\\hat{{\\bf Q}} - \\omega^2 \\rho {\\bf I}) {\\bf d} = {\\bf 0}, \\] where \\(\\hat{{\\bf Q}}(\\hat{{\\bf k}})\\) is the normalized acoustic tensor that varies depending on the bulk constitutive equation substituted for \\({\\boldsymbol \\sigma}({\\bf d})\\) . The above equation requires \\[ \\det( k^2\\hat{{\\bf Q}} - \\omega^2 \\rho {\\bf I} ) = 0 . \\] Evidently, the eigenvalues and eigenvectors of \\(\\hat{{\\bf Q}}/\\rho\\) are the permitted wave velocities squared and wave polarization, respectively, where \\[ V^2 = \\frac{\\omega^2}{k^2} \\] is the wave velocity squared. Homogenization The problem may be closed by approximating \\(\\hat{{\\bf Q}}\\) as the grain-averaged acoustic tensor, subject to a linear combination of the Voigt and Reuss homogenization schemes: \\[ \\hat{{\\bf Q}} = (1-\\alpha) \\langle\\hat{{\\bf Q}}'_{\\mathrm{Reuss}}\\rangle + \\alpha \\langle \\hat{{\\bf Q}}'_{\\mathrm{Voigt}}\\rangle . \\] The Voigt scheme ( \\(\\alpha=1\\) ) assumes the strain field is homogeneous over the polycrystal scale, whereas the Reuss scheme ( \\(\\alpha=0\\) ) assumes the stress is homogeneous. In these homogenizations, grains are therefore assumed interactionless and the bulk elastic behaviour is therefore simply the grain-orientation-averaged elastic behaviour subject to either homogeneous stress or strain assumptions over the polycrystal scale. Grain parameters The grain elastic parameters used for homogenization should be understood as the effective polycrystal values needed to reproduce experimental results; they are not the values derived from experiments on single crystals. Transversely isotropic grains Monocrystal Polycrystal If grains are approximately transversely isotropic, the grain elastic behaviour can be modelled using the transversely isotropic elastic constitutive equation . This requires specifying the grain elastic parameters \\(\\lambda'\\) , \\(\\mu'\\) , \\(\\hat{\\lambda}'\\) , \\(\\hat{\\mu}'\\) , \\(\\hat{\\gamma}'\\) , and the Voigt\u2014Reuss weight \\(\\alpha\\) . Example for glacier ice import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(4) # L=4 is sufficient here nlm = np.zeros((nlm_len), dtype=np.complex64) # array of expansion coefficients ### c-axis number distribution (nlm) from fourth-order structure tensor (a4) p = np.array([0,0,1]) # preferred c-axis direction a4 = np.einsum('i,j,k,l', p,p,p,p) # a4 if ODF = deltafunc(r-p) nlm[:sf.L4len] = sf.a4_to_nlm(a4) # determine l<=4 expansion coefficients of ODF ### Physical parameters (SI units) rho = 917 # density of ice Cij = (14.060e9, 15.240e9, 3.060e9, 7.150e9, 5.880e9) # Bennett (1968) parameters (C11,C33,C55,C12,C13) Lame_grain = sf.Cij_to_Lame_tranisotropic(Cij) # Lame parameters (lam,mu,Elam,Emu,Egam) alpha = 0.5 # Voigt--Reuss weight, where 0.5 = Hill average ### Propagation directions of interest theta, phi = np.deg2rad([90,70,]), np.deg2rad([0,10,]) # wave-vector directions (theta is colatitude, phi is longitude) ### Calculate phase velocities Vi = sf.Vi_elastic_tranisotropic(nlm, alpha, Lame_grain, rho, theta,phi) # phase velocities are V_S1=vi[0,:], V_S2=vi[1,:], V_P=vi[2,:] Evolving CPO The below animation shows directional P- and S-wave velocities for a CPO evolving under uniaxial compression along \\({\\hat {\\bf z}}\\) , relative to an isotropic CPO, when subject to lattice rotation . Orthotropic grains Monocrystal Polycrystal If grains are approximately orthotropic, the grain elastic behaviour can be modelled using the orthotropic elastic constitutive equation . This requires specifying the grain elastic parameters \\(\\lambda_{ij}'\\) , \\(\\mu_{i}'\\) , and the Voigt\u2014Reuss weight \\(\\alpha\\) . Example for olivine import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(4) # L=4 is sufficient here nlm = np.zeros((nlm_len), dtype=np.complex64) blm = np.zeros((nlm_len), dtype=np.complex64) ### Grain orientation distributions (nlm,blm) from fourth-order structure tensors vn = np.array([0,0,1]) # preferred slip-normal direction vb = np.array([1,0,0]) # preferred slip direction a4_n = np.einsum('i,j,k,l', vn,vn,vn,vn) # a4 if n/N ODF = deltafunc(r-vn) a4_b = np.einsum('i,j,k,l', vb,vb,vb,vb) # a4 if b/N ODF = deltafunc(r-vb) nlm[:sf.L4len] = sf.a4_to_nlm(a4_n) # determine l<=4 expansion coefficients of ODF blm[:sf.L4len] = sf.a4_to_nlm(a4_b) # determine l<=4 expansion coefficients of ODF ### Physical parameters (SI units) rho = 3355 # density of olivine alpha = 1 # Voigt--Reuss weight; only alpha=1 supported for now # Note the below ordering of elastic parameters assume an A-type fabric; that is, (blm,nlm,vlm) refer to the distibutions of (m1',m2',m3') axes, respectively. # If concerned with another fabric type, you will need to re-order the components of Lame_grain accordingly. Cij = (320.5e9, 196.5e9, 233.5e9, 64.0e9, 77.0e9, 78.7e9, 76.8e9, 71.6e9, 68.15e9) # Abramson (1997) parameters (C11,C22,C33,C44,C55,C66,C23,C13,C12) Lame_grain = sf.Cij_to_Lame_orthotropic(Cij) # Lame parameters (lam11,lam22,lam33, lam23,lam13,lam12, mu1,mu2,mu3) ### Propagation directions of interest theta, phi = np.deg2rad([90,70,]), np.deg2rad([0,10,]) # wave-vector directions (theta is colatitude, phi is longitude) ### Calculate phase velocities vlm = 0*nlm # estimate vlm from (blm,nlm) by passing zero array Vi = sf.Vi_elastic_orthotropic(blm,nlm,vlm, alpha, Lame_grain, rho, theta,phi) # phase velocities are V_S1=vi[0,:], V_S2=vi[1,:], V_P=vi[2,:]","title":"Wave propagation"},{"location":"wavepropagation-elastic/#elastic-wave-propagation","text":"","title":"Elastic wave propagation"},{"location":"wavepropagation-elastic/#problem","text":"We seek plane wave solutions of the Cauchy-Navier equation of motion \\[ \\nabla\\cdot {\\boldsymbol \\sigma} = \\rho \\frac{\\partial^2 {\\bf d}}{\\partial t^2}, \\] where \\({\\boldsymbol \\sigma}\\) is the bulk stress tensor, \\({\\bf d}\\) is the displacement field, and \\(\\rho\\) the mass density. Substituting \\({\\bf d}\\) for a plane wave solution, \\({\\bf d} = {\\bf d}_0 \\exp[i({\\bf k}\\cdot {\\bf x} - \\omega t)]\\) , the problem reduces to \\[ (k^2\\hat{{\\bf Q}} - \\omega^2 \\rho {\\bf I}) {\\bf d} = {\\bf 0}, \\] where \\(\\hat{{\\bf Q}}(\\hat{{\\bf k}})\\) is the normalized acoustic tensor that varies depending on the bulk constitutive equation substituted for \\({\\boldsymbol \\sigma}({\\bf d})\\) . The above equation requires \\[ \\det( k^2\\hat{{\\bf Q}} - \\omega^2 \\rho {\\bf I} ) = 0 . \\] Evidently, the eigenvalues and eigenvectors of \\(\\hat{{\\bf Q}}/\\rho\\) are the permitted wave velocities squared and wave polarization, respectively, where \\[ V^2 = \\frac{\\omega^2}{k^2} \\] is the wave velocity squared. Homogenization The problem may be closed by approximating \\(\\hat{{\\bf Q}}\\) as the grain-averaged acoustic tensor, subject to a linear combination of the Voigt and Reuss homogenization schemes: \\[ \\hat{{\\bf Q}} = (1-\\alpha) \\langle\\hat{{\\bf Q}}'_{\\mathrm{Reuss}}\\rangle + \\alpha \\langle \\hat{{\\bf Q}}'_{\\mathrm{Voigt}}\\rangle . \\] The Voigt scheme ( \\(\\alpha=1\\) ) assumes the strain field is homogeneous over the polycrystal scale, whereas the Reuss scheme ( \\(\\alpha=0\\) ) assumes the stress is homogeneous. In these homogenizations, grains are therefore assumed interactionless and the bulk elastic behaviour is therefore simply the grain-orientation-averaged elastic behaviour subject to either homogeneous stress or strain assumptions over the polycrystal scale. Grain parameters The grain elastic parameters used for homogenization should be understood as the effective polycrystal values needed to reproduce experimental results; they are not the values derived from experiments on single crystals.","title":"Problem"},{"location":"wavepropagation-elastic/#transversely-isotropic-grains","text":"Monocrystal Polycrystal If grains are approximately transversely isotropic, the grain elastic behaviour can be modelled using the transversely isotropic elastic constitutive equation . This requires specifying the grain elastic parameters \\(\\lambda'\\) , \\(\\mu'\\) , \\(\\hat{\\lambda}'\\) , \\(\\hat{\\mu}'\\) , \\(\\hat{\\gamma}'\\) , and the Voigt\u2014Reuss weight \\(\\alpha\\) .","title":"Transversely isotropic grains"},{"location":"wavepropagation-elastic/#example-for-glacier-ice","text":"import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(4) # L=4 is sufficient here nlm = np.zeros((nlm_len), dtype=np.complex64) # array of expansion coefficients ### c-axis number distribution (nlm) from fourth-order structure tensor (a4) p = np.array([0,0,1]) # preferred c-axis direction a4 = np.einsum('i,j,k,l', p,p,p,p) # a4 if ODF = deltafunc(r-p) nlm[:sf.L4len] = sf.a4_to_nlm(a4) # determine l<=4 expansion coefficients of ODF ### Physical parameters (SI units) rho = 917 # density of ice Cij = (14.060e9, 15.240e9, 3.060e9, 7.150e9, 5.880e9) # Bennett (1968) parameters (C11,C33,C55,C12,C13) Lame_grain = sf.Cij_to_Lame_tranisotropic(Cij) # Lame parameters (lam,mu,Elam,Emu,Egam) alpha = 0.5 # Voigt--Reuss weight, where 0.5 = Hill average ### Propagation directions of interest theta, phi = np.deg2rad([90,70,]), np.deg2rad([0,10,]) # wave-vector directions (theta is colatitude, phi is longitude) ### Calculate phase velocities Vi = sf.Vi_elastic_tranisotropic(nlm, alpha, Lame_grain, rho, theta,phi) # phase velocities are V_S1=vi[0,:], V_S2=vi[1,:], V_P=vi[2,:] Evolving CPO The below animation shows directional P- and S-wave velocities for a CPO evolving under uniaxial compression along \\({\\hat {\\bf z}}\\) , relative to an isotropic CPO, when subject to lattice rotation .","title":"Example for glacier ice"},{"location":"wavepropagation-elastic/#orthotropic-grains","text":"Monocrystal Polycrystal If grains are approximately orthotropic, the grain elastic behaviour can be modelled using the orthotropic elastic constitutive equation . This requires specifying the grain elastic parameters \\(\\lambda_{ij}'\\) , \\(\\mu_{i}'\\) , and the Voigt\u2014Reuss weight \\(\\alpha\\) .","title":"Orthotropic grains"},{"location":"wavepropagation-elastic/#example-for-olivine","text":"import numpy as np from specfabpy import specfab as sf lm, nlm_len = sf.init(4) # L=4 is sufficient here nlm = np.zeros((nlm_len), dtype=np.complex64) blm = np.zeros((nlm_len), dtype=np.complex64) ### Grain orientation distributions (nlm,blm) from fourth-order structure tensors vn = np.array([0,0,1]) # preferred slip-normal direction vb = np.array([1,0,0]) # preferred slip direction a4_n = np.einsum('i,j,k,l', vn,vn,vn,vn) # a4 if n/N ODF = deltafunc(r-vn) a4_b = np.einsum('i,j,k,l', vb,vb,vb,vb) # a4 if b/N ODF = deltafunc(r-vb) nlm[:sf.L4len] = sf.a4_to_nlm(a4_n) # determine l<=4 expansion coefficients of ODF blm[:sf.L4len] = sf.a4_to_nlm(a4_b) # determine l<=4 expansion coefficients of ODF ### Physical parameters (SI units) rho = 3355 # density of olivine alpha = 1 # Voigt--Reuss weight; only alpha=1 supported for now # Note the below ordering of elastic parameters assume an A-type fabric; that is, (blm,nlm,vlm) refer to the distibutions of (m1',m2',m3') axes, respectively. # If concerned with another fabric type, you will need to re-order the components of Lame_grain accordingly. Cij = (320.5e9, 196.5e9, 233.5e9, 64.0e9, 77.0e9, 78.7e9, 76.8e9, 71.6e9, 68.15e9) # Abramson (1997) parameters (C11,C22,C33,C44,C55,C66,C23,C13,C12) Lame_grain = sf.Cij_to_Lame_orthotropic(Cij) # Lame parameters (lam11,lam22,lam33, lam23,lam13,lam12, mu1,mu2,mu3) ### Propagation directions of interest theta, phi = np.deg2rad([90,70,]), np.deg2rad([0,10,]) # wave-vector directions (theta is colatitude, phi is longitude) ### Calculate phase velocities vlm = 0*nlm # estimate vlm from (blm,nlm) by passing zero array Vi = sf.Vi_elastic_orthotropic(blm,nlm,vlm, alpha, Lame_grain, rho, theta,phi) # phase velocities are V_S1=vi[0,:], V_S2=vi[1,:], V_P=vi[2,:]","title":"Example for olivine"},{"location":"wavepropagation-electromagnetic/","text":"Electromagnetic wave propagation Problem We seek plane wave solutions to Maxwell's equations in a non-conducting, source-free, anisotropic linear dielectric medium \\[ \\nabla^2 {\\bf E} = \\mu {\\boldsymbol \\epsilon} \\frac{\\partial^2 {\\bf E}}{\\partial t^2}, \\] where \\({\\bf E}\\) is the electric field, \\({\\boldsymbol \\epsilon}\\) is the bulk dielectric permittivity tensor, and \\(\\mu\\) the bulk isotropic permeability of the medium. Substituting \\({\\bf E}\\) for a plane wave solution, \\({\\bf E} = {\\bf E}_0 \\exp[i({\\bf k}\\cdot {\\bf x} - \\omega t)]\\) , the problem reduces to \\[ ({\\bf K} + \\omega^2 \\mu {\\boldsymbol \\epsilon}) {\\bf E} = {\\bf 0}, \\] where \\({\\bf K} = {\\bf k}\\times{\\bf k} \\times\\) is the matrix representation of the twice-applied cross product. The above equation requires \\[ \\det( {\\bf K} + \\omega^2 \\mu {\\boldsymbol \\epsilon} ) = 0 . \\] Evidently, the eigenvalues and eigenvectors of \\({\\boldsymbol \\epsilon}^{-1} {\\bf K}/(\\mu k^2)\\) are the permitted wave velocities squared and wave polarization, respectively, where \\[ V^2 = \\frac{\\omega^2}{k^2} \\] is the wave velocity squared. Homogenization If wave lengths are much longer than the average grain size, the problem can be closed by approximating the bulk polycrystalline permittivity tensor by the grain-averaged permittivity tensor \\[{\\boldsymbol \\epsilon} = \\langle {\\boldsymbol \\epsilon}' \\rangle,\\] constructed by averaging over all grain orientations (over the CPO). Transversely isotropic grains Monocrystal Polycrystal If grains are approximately transversely isotropic w.r.t. the symmetry axis \\({\\bf m}'\\) , the dielectric permittivity tensor of a single crystal can be written as $$ {\\boldsymbol \\epsilon}' = (2\\epsilon_{t}' + \\epsilon_{m}') \\frac{\\bf I}{3} + (\\epsilon_{m}'-\\epsilon_{t}') \\left( {\\bf m}'^2 - \\frac{\\bf I}{3} \\right), $$ where \\(\\epsilon_{m}'\\) and \\(\\epsilon_{t}'\\) are the permittivities parallel and perpendicular to the symmetry axis. In this case, the grain-averaged permitivity is simply \\[ {\\boldsymbol \\epsilon} = (2\\epsilon_{t}' + \\epsilon_{m}') \\frac{\\bf I}{3} + (\\epsilon_{m}'-\\epsilon_{t}') \\left( \\langle {\\bf m}'^2 \\rangle - \\frac{\\bf I}{3} \\right) , \\] where \\(\\langle {\\bf m}'^2 \\rangle\\) is the second-order structure tensor (aka \\({\\bf a}^{(2)}\\) ). Example for glacier ice To be documented. Orthotropic grains Not yet supported.","title":"Wave propagation"},{"location":"wavepropagation-electromagnetic/#electromagnetic-wave-propagation","text":"","title":"Electromagnetic wave propagation"},{"location":"wavepropagation-electromagnetic/#problem","text":"We seek plane wave solutions to Maxwell's equations in a non-conducting, source-free, anisotropic linear dielectric medium \\[ \\nabla^2 {\\bf E} = \\mu {\\boldsymbol \\epsilon} \\frac{\\partial^2 {\\bf E}}{\\partial t^2}, \\] where \\({\\bf E}\\) is the electric field, \\({\\boldsymbol \\epsilon}\\) is the bulk dielectric permittivity tensor, and \\(\\mu\\) the bulk isotropic permeability of the medium. Substituting \\({\\bf E}\\) for a plane wave solution, \\({\\bf E} = {\\bf E}_0 \\exp[i({\\bf k}\\cdot {\\bf x} - \\omega t)]\\) , the problem reduces to \\[ ({\\bf K} + \\omega^2 \\mu {\\boldsymbol \\epsilon}) {\\bf E} = {\\bf 0}, \\] where \\({\\bf K} = {\\bf k}\\times{\\bf k} \\times\\) is the matrix representation of the twice-applied cross product. The above equation requires \\[ \\det( {\\bf K} + \\omega^2 \\mu {\\boldsymbol \\epsilon} ) = 0 . \\] Evidently, the eigenvalues and eigenvectors of \\({\\boldsymbol \\epsilon}^{-1} {\\bf K}/(\\mu k^2)\\) are the permitted wave velocities squared and wave polarization, respectively, where \\[ V^2 = \\frac{\\omega^2}{k^2} \\] is the wave velocity squared. Homogenization If wave lengths are much longer than the average grain size, the problem can be closed by approximating the bulk polycrystalline permittivity tensor by the grain-averaged permittivity tensor \\[{\\boldsymbol \\epsilon} = \\langle {\\boldsymbol \\epsilon}' \\rangle,\\] constructed by averaging over all grain orientations (over the CPO).","title":"Problem"},{"location":"wavepropagation-electromagnetic/#transversely-isotropic-grains","text":"Monocrystal Polycrystal If grains are approximately transversely isotropic w.r.t. the symmetry axis \\({\\bf m}'\\) , the dielectric permittivity tensor of a single crystal can be written as $$ {\\boldsymbol \\epsilon}' = (2\\epsilon_{t}' + \\epsilon_{m}') \\frac{\\bf I}{3} + (\\epsilon_{m}'-\\epsilon_{t}') \\left( {\\bf m}'^2 - \\frac{\\bf I}{3} \\right), $$ where \\(\\epsilon_{m}'\\) and \\(\\epsilon_{t}'\\) are the permittivities parallel and perpendicular to the symmetry axis. In this case, the grain-averaged permitivity is simply \\[ {\\boldsymbol \\epsilon} = (2\\epsilon_{t}' + \\epsilon_{m}') \\frac{\\bf I}{3} + (\\epsilon_{m}'-\\epsilon_{t}') \\left( \\langle {\\bf m}'^2 \\rangle - \\frac{\\bf I}{3} \\right) , \\] where \\(\\langle {\\bf m}'^2 \\rangle\\) is the second-order structure tensor (aka \\({\\bf a}^{(2)}\\) ).","title":"Transversely isotropic grains"},{"location":"wavepropagation-electromagnetic/#example-for-glacier-ice","text":"To be documented.","title":"Example for glacier ice"},{"location":"wavepropagation-electromagnetic/#orthotropic-grains","text":"Not yet supported.","title":"Orthotropic grains"}]} \ No newline at end of file diff --git a/sitemap.xml b/sitemap.xml index 922a1899..a07fb328 100644 --- a/sitemap.xml +++ b/sitemap.xml @@ -2,82 +2,82 @@ None - 2024-01-17 + 2024-01-21 daily None - 2024-01-17 + 2024-01-21 daily None - 2024-01-17 + 2024-01-21 daily None - 2024-01-17 + 2024-01-21 daily None - 2024-01-17 + 2024-01-21 daily None - 2024-01-17 + 2024-01-21 daily None - 2024-01-17 + 2024-01-21 daily None - 2024-01-17 + 2024-01-21 daily None - 2024-01-17 + 2024-01-21 daily None - 2024-01-17 + 2024-01-21 daily None - 2024-01-17 + 2024-01-21 daily None - 2024-01-17 + 2024-01-21 daily None - 2024-01-17 + 2024-01-21 daily None - 2024-01-17 + 2024-01-21 daily None - 2024-01-17 + 2024-01-21 daily None - 2024-01-17 + 2024-01-21 daily \ No newline at end of file diff --git a/sitemap.xml.gz b/sitemap.xml.gz index 427f1644..4d08e01e 100644 Binary files a/sitemap.xml.gz and b/sitemap.xml.gz differ diff --git a/wavepropagation-elastic/index.html b/wavepropagation-elastic/index.html index 2f90c3be..7d65a4c7 100644 --- a/wavepropagation-elastic/index.html +++ b/wavepropagation-elastic/index.html @@ -252,9 +252,12 @@

Example for olivine

### Physical parameters (SI units) rho = 3355 # density of olivine +alpha = 1 # Voigt--Reuss weight; only alpha=1 supported for now +# Note the below ordering of elastic parameters assume an A-type fabric; that is, (blm,nlm,vlm) refer to the distibutions of (m1',m2',m3') axes, respectively. +# If concerned with another fabric type, you will need to re-order the components of Lame_grain accordingly. Cij = (320.5e9, 196.5e9, 233.5e9, 64.0e9, 77.0e9, 78.7e9, 76.8e9, 71.6e9, 68.15e9) # Abramson (1997) parameters (C11,C22,C33,C44,C55,C66,C23,C13,C12) Lame_grain = sf.Cij_to_Lame_orthotropic(Cij) # Lame parameters (lam11,lam22,lam33, lam23,lam13,lam12, mu1,mu2,mu3) -alpha = 1 # Voigt--Reuss weight; only alpha=1 supported for now + ### Propagation directions of interest theta, phi = np.deg2rad([90,70,]), np.deg2rad([0,10,]) # wave-vector directions (theta is colatitude, phi is longitude)