def test_from_voigt(self): with self.assertRaises(ValueError): TensorBase.from_voigt([[59.33, 28.08, 28.08, 0], [28.08, 59.31, 28.07, 0], [28.08, 28.07, 59.32, 0, 0], [0, 0, 0, 26.35, 0], [0, 0, 0, 0, 26.35]]) # Rank 4 TensorBase.from_voigt([[59.33, 28.08, 28.08, 0, 0, 0], [28.08, 59.31, 28.07, 0, 0, 0], [28.08, 28.07, 59.32, 0, 0, 0], [0, 0, 0, 26.35, 0, 0], [0, 0, 0, 0, 26.35, 0], [0, 0, 0, 0, 0, 26.35]])
def test_from_voigt(self): with self.assertRaises(ValueError): TensorBase.from_voigt([[59.33, 28.08, 28.08, 0], [28.08, 59.31, 28.07, 0], [28.08, 28.07, 59.32, 0, 0], [0, 0, 0, 26.35, 0], [0, 0, 0, 0, 26.35]]) # Rank 4 TensorBase.from_voigt([[59.33, 28.08, 28.08, 0, 0, 0], [28.08, 59.31, 28.07, 0, 0, 0], [28.08, 28.07, 59.32, 0, 0, 0], [0, 0, 0, 26.35, 0, 0], [0, 0, 0, 0, 26.35, 0], [0, 0, 0, 0, 0, 26.35]])
def toec_fit(strains, stresses, eq_stress = None, zero_crit=1e-10): """ A third-order elastic constant fitting function based on central-difference derivatives with respect to distinct strain states. The algorithm is summarized as follows: 1. Identify distinct strain states as sets of indices for which nonzero strain values exist, typically [(0), (1), (2), (3), (4), (5), (0, 1) etc.] 2. For each strain state, find and sort strains and stresses by strain value. 3. Find first and second derivatives of each stress with respect to scalar variable corresponding to the smallest perturbation in the strain. 4. Use the pseudoinverse of a matrix-vector expression corresponding to the parameterized stress-strain relationship and multiply that matrix by the respective calculated first or second derivatives from the previous step. 5. Place the calculated second and third-order elastic constants appropriately. Args: strains (nx3x3 array-like): Array of 3x3 strains to use in fitting of TOEC and SOEC stresses (nx3x3 array-like): Array of 3x3 stresses to use in fitting of TOEC and SOEC. These should be PK2 stresses. eq_stress (3x3 array-like): stress corresponding to equilibrium strain (i. e. "0" strain state). If not specified, function will try to find the state in the list of provided stresses and strains. If not found, defaults to 0. zero_crit (float): value for which strains below are ignored in identifying strain states. """ if len(stresses) != len(strains): raise ValueError("Length of strains and stresses are not equivalent") vstresses = np.array([Stress(stress).voigt for stress in stresses]) vstrains = np.array([Strain(strain).voigt for strain in strains]) vstrains[np.abs(vstrains) < zero_crit] = 0 # Try to find eq_stress if not specified if eq_stress is not None: veq_stress = Stress(eq_stress).voigt else: veq_stress = vstresses[np.all(vstrains==0, axis=1)] if veq_stress: if np.shape(veq_stress) > 1 and not \ (abs(veq_stress - veq_stress[0]) < 1e-8).all(): raise ValueError("Multiple stresses found for equilibrium strain" " state, please specify equilibrium stress or " " remove extraneous stresses.") veq_stress = veq_stress[0] else: veq_stress = np.zeros(6) # Collect independent strain states: independent = set([tuple(np.nonzero(vstrain)[0].tolist()) for vstrain in vstrains]) strain_states = [] dsde = np.zeros((6, len(independent))) d2sde2 = np.zeros((6, len(independent))) for n, ind in enumerate(independent): # match strains with templates template = np.zeros(6, dtype=bool) np.put(template, ind, True) template = np.tile(template, [vstresses.shape[0], 1]) mode = (template == (np.abs(vstrains) > 1e-10)).all(axis=1) mstresses = vstresses[mode] mstrains = vstrains[mode] # add zero strain state mstrains = np.vstack([mstrains, np.zeros(6)]) mstresses = np.vstack([mstresses, np.zeros(6)]) # sort strains/stresses by strain values mstresses = mstresses[mstrains[:, ind[0]].argsort()] mstrains = mstrains[mstrains[:, ind[0]].argsort()] strain_states.append(mstrains[-1] / \ np.min(mstrains[-1][np.nonzero(mstrains[0])])) diff = np.diff(mstrains, axis=0) if not (abs(diff - diff[0]) < 1e-8).all(): raise ValueError("Stencil for strain state {} must be odd-sampling" " centered at 0.".format(ind)) h = np.min(diff[np.nonzero(diff)]) coef1 = central_diff_weights(len(mstresses), 1) coef2 = central_diff_weights(len(mstresses), 2) if eq_stress is not None: mstresses[len(mstresses) // 2] = veq_stress dsde[:, n] = np.dot(np.transpose(mstresses), coef1) / h d2sde2[:, n] = np.dot(np.transpose(mstresses), coef2) / h**2 m2i, m3i = generate_pseudo(strain_states) s2vec = np.ravel(dsde.T) c2vec = np.dot(m2i, s2vec) c2 = np.zeros((6, 6)) c2[np.triu_indices(6)] = c2vec c2 = c2 + c2.T - np.diag(np.diag(c2)) c3 = np.zeros((6, 6, 6)) s3vec = np.ravel(d2sde2.T) c3vec = np.dot(m3i, s3vec) list_indices = list(itertools.combinations_with_replacement(range(6), r=3)) indices_ij = itertools.combinations_with_replacement(range(6), r=3) indices = list(itertools.combinations_with_replacement(range(6), r=3)) for n, (i, j, k) in enumerate(indices): c3[i,j,k] = c3[i,k,j] = c3[j,i,k] = c3[j,k,i] = \ c3[k,i,j] = c3[k,j,i] = c3vec[n] return TensorBase.from_voigt(c2), TensorBase.from_voigt(c3)