ruspectroscopy-tools 0.0.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 jacubillos10
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.1
2
+ Name: ruspectroscopy_tools
3
+ Version: 0.0.1
4
+ Summary: A simple tool that generates Gamma matrix and E matrix, given the elastic constants, mass, dimansions and shape of a sample, in Resonant Ultrasound Spectroscopy. Use scipy.linalg.eigh(...) to solve the forward problem
5
+ Home-page: https://github.com/jacubillos10/RUSpectroscopy_Tools
6
+ Author: Alejandro Cubillos
7
+ Author-email: alejandro4cm@outlook.com
8
+ License: MIT
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.6
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+
16
+ # RUSpectroscopy_tools
17
+
18
+ A C extension module for generating Gamma and Epsilon matrices, in Resonant Ultrasound Spectroscopy.
19
+
20
+ ## Authors
21
+
22
+ - Alejandro Cubillos - alejandro4cm@outlook.com
23
+ - Manuela Rivas
24
+ - Julián Rincón
25
+
26
+ ## Import:
27
+ ```python
28
+ from rusmodules import rus
29
+ ```
30
+
31
+ ## Usage Example
32
+ ```python
33
+ gamma = rus.gamma_matrix(N, C, geometry, shape)
34
+ E = rus.E_matrix(N, shape)
35
+ ```
36
+
37
+ # Where:
38
+ * N <int> represents the maximim grade of the polynomials of the basis functions
39
+ * C <np.array> 6x6 matrix with the elastic constants
40
+ * geometry <np.array> (3,) shape array containing the dimensions of the sample
41
+ * shape <int> 0 for parallelepiped, 1 for cylinder and 2 to spheroid
42
+
43
+ Solve forward problem with scipy:
44
+ Eigenvalues return $m\omega^2$
45
+ ```python
46
+ eigenvals, eigenvects = scipy.linalg.eigh(a = gamma, b = E)
47
+ omega = (eigenvals[6:]/m)**(1/2) #remember the fisrt 6 eigenvalues must be near zero
48
+ frequencies = omega/(2*np.pi)
49
+ ```
50
+
51
+ ## Installation:
52
+ ```bash
53
+ pip3 install ruspectroscopy_tools
@@ -0,0 +1,38 @@
1
+ # RUSpectroscopy_tools
2
+
3
+ A C extension module for generating Gamma and Epsilon matrices, in Resonant Ultrasound Spectroscopy.
4
+
5
+ ## Authors
6
+
7
+ - Alejandro Cubillos - alejandro4cm@outlook.com
8
+ - Manuela Rivas
9
+ - Julián Rincón
10
+
11
+ ## Import:
12
+ ```python
13
+ from rusmodules import rus
14
+ ```
15
+
16
+ ## Usage Example
17
+ ```python
18
+ gamma = rus.gamma_matrix(N, C, geometry, shape)
19
+ E = rus.E_matrix(N, shape)
20
+ ```
21
+
22
+ # Where:
23
+ * N <int> represents the maximim grade of the polynomials of the basis functions
24
+ * C <np.array> 6x6 matrix with the elastic constants
25
+ * geometry <np.array> (3,) shape array containing the dimensions of the sample
26
+ * shape <int> 0 for parallelepiped, 1 for cylinder and 2 to spheroid
27
+
28
+ Solve forward problem with scipy:
29
+ Eigenvalues return $m\omega^2$
30
+ ```python
31
+ eigenvals, eigenvects = scipy.linalg.eigh(a = gamma, b = E)
32
+ omega = (eigenvals[6:]/m)**(1/2) #remember the fisrt 6 eigenvalues must be near zero
33
+ frequencies = omega/(2*np.pi)
34
+ ```
35
+
36
+ ## Installation:
37
+ ```bash
38
+ pip3 install ruspectroscopy_tools
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuptools", "wheel", "numpy", "schipy"]
3
+ build-backend = "setuptools.build_meta"
File without changes
@@ -0,0 +1,49 @@
1
+ import numpy as np
2
+ from numba import njit, types
3
+ from numba.pycc import CC
4
+ cc = CC('data_generator')
5
+
6
+ @njit("f8[:,:](f8,f8,i8)")
7
+ @cc.export("generate_C_matrix", "f8[:,:](f8,f8,i8)")
8
+ def generate_C_matrix(C_min, C_max, crystal_structure):
9
+ C = np.zeros((6,6))
10
+ C_prc = np.zeros(3)
11
+ C_sec = np.zeros(6)
12
+ C_ter = np.zeros(12)
13
+ d_struc = np.array([[3,6],[2,4],[1,2],[1,1]])
14
+ n_p = d_struc[crystal_structure,0]
15
+ n_s = d_struc[crystal_structure,1]
16
+ C_prc_raw = np.random.uniform(C_min, C_max, n_p)
17
+ C_max_sec = 0.5*min(C_prc)
18
+ C_sec_raw = np.random.uniform(C_min, C_max_sec, n_s)
19
+ if crystal_structure == 1:
20
+ C_prc[0:2] = C_prc_raw[0]
21
+ C_prc[2] = C_prc_raw[1]
22
+ C_sec[0] = C_sec_raw[0]
23
+ C_sec[1:3] = C_sec_raw[1]
24
+ C_sec[3:5] = C_sec_raw[2]
25
+ C_sec[5] = C_sec_raw[3]
26
+ elif crystal_structure == 2:
27
+ C_prc[:] = C_prc_raw[0]
28
+ C_sec[0:3] = C_sec_raw[0]
29
+ C_sec[3:] = C_sec_raw[1]
30
+ elif crystal_structure == 3:
31
+ C_prc[:] = C_prc_raw[0]
32
+ C_sec[0:3] = C_prc_raw[0] - 2*C_sec_raw[0]
33
+ C_sec[3:] = C_sec_raw[0]
34
+ else:
35
+ C_prc = C_prc_raw
36
+ C_sec = C_sec_raw
37
+ #end if
38
+ for i in range(3):
39
+ for j in range(3):
40
+ C[i,j] = C_prc[i] if i == j else C_sec[i+j-1]
41
+ C[i+3,j+3] = C_sec[i+3] if i == j else 0
42
+ #fin for
43
+ #fin if
44
+
45
+ return C
46
+ #end function
47
+
48
+ if __name__ == "__main__":
49
+ cc.compile()
@@ -0,0 +1,208 @@
1
+ import numpy as np
2
+ from numba import njit, types
3
+ from numba.pycc import CC
4
+
5
+ cc = CC('rus_old')
6
+ @njit("f8(i8)")
7
+ @cc.export("fact2", "f8(i8)")
8
+ def fact2(N):
9
+ if N < -1:
10
+ raise ArithmeticError("Está entrando en el doble factorial un númerno menor a -1")
11
+ elif (N == 0 or N == -1 or N == 1):
12
+ return 1
13
+ else:
14
+ prod = 1.0
15
+ for i in range(N,1,-2):
16
+ prod = prod*i
17
+ #fin for
18
+ return prod
19
+ #fin if
20
+ #fin función
21
+
22
+ @njit("i8(i8,i8)")
23
+ @cc.export("it_c", "i8(i8,i8)")
24
+ def it_c(i,j):
25
+ """
26
+ This function changes the double index convention for single index convention in the elastic constant matrix C.
27
+ 00 is changed to 0, 11 is changed to 1, 22 is changed to 2,
28
+ 01 or 10 is changed to 5
29
+ 02 or 20 is changed to 4
30
+ 12 or 21 is changed to 3
31
+
32
+ @Input i <int>: first index to be changed
33
+ @Input j <int>: second index to be changed
34
+ @Return index <int>: New index
35
+ """
36
+ if i == j:
37
+ index = i
38
+ else:
39
+ index = 6 - (i + j)
40
+ #fin if
41
+ return index
42
+ #fin función
43
+
44
+ @njit("f8(i8[:],i8[:],i8,i8,i8,i8,f8[:,:],f8[:],i8)")
45
+ @cc.export("generate_term_in_gamma_matrix_element", "f8(i8[:],i8[:],i8,i8,i8,i8,f8[:,:],f8[:],i8)")
46
+ def generate_term_in_ſ(exp_index1, exp_index2, i1, i2, j1, j2, C, geo_par, options):
47
+ """
48
+ This function generates one term of the sum composing an element of the matrix ſ. See equation 11 in Leisure 1997. Be sure geo_par
49
+ to be an array of floats NOT INTEGERS!!
50
+ @Input exp_index1 <np.array>: Exponents lambda, mu and nu of the p element in the matrix and i1 index
51
+ @Input exp_index2 <np.array>: Exponents lambda, mu and nu of the q element in the matrix and i2 index
52
+ @Input i1 <int>: index i of the expresion in eq 11. 0 to x, 1 to y, 2 to z
53
+ @Input i2 <int>: index i' of the expresion in eq 11. 0 to x, 1 to y, 2 to z
54
+ @Input j1 <int>: index j of the sum in equation 11. 0 to x, 1 to y, 2 to z
55
+ @Input j2 <int>: index j' of the sum in equation 11. 0 to x, 1 to y, 2 to z
56
+ @Input C <np.array>: Matrix with the elastic constants
57
+ @Input geo_par <np.array>: Geometric parameters of the sample, such as Lx, Ly, Lz. Be sure to enter a array of floats NOT INTEGERS!
58
+ @Return <float>: Returns a term in the equation 11 of Leisure 1997
59
+ """
60
+ array_j1 = np.zeros(3)
61
+ array_j2 = np.zeros(3)
62
+ array_j1[j1] = 1
63
+ array_j2[j2] = 1
64
+ coeff = exp_index1 + exp_index2 + 1 - array_j1 - array_j2
65
+ Q = (1 - (-1)**coeff[0])*(1 - (-1)**coeff[1])*(1 - (-1)**coeff[2])
66
+ S = exp_index1[j1]*exp_index2[j2]
67
+ #geo_par = np.array(geo_par, dtype = float)
68
+ if Q == 0 or S == 0:
69
+ return 0
70
+ #fin if
71
+ if options == 1:
72
+ R = (fact2(coeff[0] - 2)*fact2(coeff[1] - 2))/(fact2(coeff[0] + coeff[1])*coeff[2])
73
+ alpha = np.pi/4
74
+ elif options == 2:
75
+ R = (fact2(coeff[0] - 2)*fact2(coeff[1] - 2)*fact2(coeff[2] - 2))/(fact2(sum(coeff)))
76
+ alpha = np.pi/6
77
+ else:
78
+ R = 1/(coeff[0]*coeff[1]*coeff[2])
79
+ alpha = 1
80
+ #fin if
81
+ #P = 4*C[it_c(i1,j1),it_c(i2,j2)]/(geo_par[j1]*geo_par[j2])
82
+ if j1 == j2:
83
+ beta = np.prod(geo_par)/(geo_par[j1]**2)
84
+ else:
85
+ beta = geo_par[3-j1-j2]
86
+ #fin if
87
+ P = 4*alpha*C[it_c(i1,j1),it_c(i2,j2)]*beta
88
+ return P*Q*S*R
89
+ #fin función
90
+
91
+ @njit("f8(i8,i8,i8[:],i8[:],f8[:,:],f8[:],i8)")
92
+ @cc.export("generate_matrix_element_gamma", "f8(i8,i8,i8[:],i8[:],f8[:,:],f8[:],i8)")
93
+ def generate_matrix_element_ſ(i1, i2, exp_index1, exp_index2, C, geo_par, options):
94
+ """
95
+ This function generates a matrix element of ſ, given two axis indexes (0 is x, 1 is y, 2 is z), two arrays of indexes which
96
+ Contain the exponents of the power series, the elastic constants and the geometric parameters
97
+ @Input i1 <int>: index i of the expresion in eq 11. 0 to x, 1 to y, 2 to z
98
+ @Input i2 <int>: index i' of the expresion in eq 11. 0 to x, 1 to y, 2 to z
99
+ @Input exp_index1 <np.array>: Exponents lambda, mu and nu of the p element in the matrix and i1 index
100
+ @Input exp_index2 <np.array>: Exponents lambda, mu and nu of the q element in the matrix and i2 index
101
+ @Input C <np.array>: Matrix with the elastic constants
102
+ @Input geo_par <np.array>: Geometric parameters of the sample, such as Lx, Ly, Lz. Be sure to enter a array of floats NOT INTEGERS!
103
+ @Return <float>: Returns a matrix element ſ
104
+ """
105
+ acum = 0
106
+ for j1 in range(3):
107
+ for j2 in range(3):
108
+ acum += generate_term_in_ſ(exp_index1, exp_index2, i1, i2, j1, j2, C, geo_par, options)
109
+ # fin for
110
+ #fin for
111
+ return acum
112
+ #fin función
113
+
114
+ @njit("f8(i8,i8,i8[:],i8[:],i8)")
115
+ @cc.export("generate_matrix_element_E", "f8(i8,i8,i8[:],i8[:],i8)")
116
+ def generate_matrix_element_E(i1, i2, exp_index1, exp_index2, options):
117
+ """
118
+ This function generates a matrix element of E, given two axis indexes (0 is x, 1 is y, 2 is z), two arrays of indexes which
119
+ contain the exponents of the power series, the elastic constants and the geometric parameters.
120
+ """
121
+ if i1 != i2:
122
+ return 0
123
+ else:
124
+ coeff = exp_index1 + exp_index2 + 1
125
+ Q = (1 - (-1)**coeff[0])*(1 - (-1)**coeff[1])*(1 - (-1)**coeff[2])
126
+ if Q == 0:
127
+ return 0
128
+ #fin if
129
+ if options == 1:
130
+ R = (fact2(coeff[0] - 2)*fact2(coeff[1] - 2))/(fact2(coeff[0] + coeff[1])*coeff[2])
131
+ elif options == 2:
132
+ R = (fact2(coeff[0] - 2)*fact2(coeff[1] - 2)*fact2(coeff[2] - 2))/(fact2(sum(coeff)))
133
+ else:
134
+ R = 1/(coeff[0]*coeff[1]*coeff[2])
135
+ #fin if
136
+ #print("Numba coeff: ", coeff, " fact 0: ", fact2(coeff[0]), " fact 1: ", fact2(coeff[1]), " fact 2: ", fact2(coeff[2]))
137
+ #print("Operacion 1 numba: ", (fact2(coeff[0] - 2)*fact2(coeff[1] - 2)))
138
+ #print("Operacion 2 numba: ", (fact2(coeff[0] + coeff[1])*coeff[2]))
139
+ #print("numba's 35!!= ", fact2(35))
140
+ return Q*R
141
+ #fin if
142
+ #fin funcion
143
+
144
+ @njit("i8[:,:](i8)")
145
+ @cc.export("generate_combinations", "i8[:,:](i8)")
146
+ def generate_combinations(N):
147
+ R = int((1/6) * (N + 1) * (N + 2) * (N + 3))
148
+ combi = np.zeros((R,3), dtype=np.int64)
149
+ l = 0
150
+ for n in range(N + 1):
151
+ for i in range(n + 1):
152
+ for j in range(n + 1 - i):
153
+ k = n - i - j
154
+ combi[l,:] = np.array([i, j, k])
155
+ l += 1
156
+ #fin for
157
+ #fin for
158
+ return combi
159
+ #fin funcion
160
+
161
+ @cc.export("E_matrix", "f8[:,:](i8,i8)")
162
+ def E_matrix(N, options):
163
+ comb = generate_combinations(N)
164
+ R = len(comb[:,0])
165
+ E = np.zeros((3*R,3*R), dtype = np.float64)
166
+ for i in range(3):
167
+ for j in range(3):
168
+ for lm in range(R):
169
+ for lf in range(R):
170
+ exp_index1 = comb[lm,:]
171
+ exp_index2 = comb[lf,:]
172
+ E[(i*R + lm), (j*R + lf)] = generate_matrix_element_E(i, j, exp_index1, exp_index2, options)
173
+ #fin for
174
+ #fin for
175
+ #fin for
176
+ #fin for
177
+ return E
178
+ #fin función
179
+
180
+ @cc.export("gamma_matrix", "f8[:,:](i8,f8[:,:],f8[:],i8)")
181
+ def gamma_matrix(N, C, geo_par, options):
182
+ comb = generate_combinations(N)
183
+ R = len(comb[:,0])
184
+ gamma = np.zeros((3*R,3*R), dtype = np.float64)
185
+ for i in range(3):
186
+ for j in range(3):
187
+ for lm in range(R):
188
+ for lf in range(R):
189
+ exp_index1 = comb[lm,:]
190
+ exp_index2 = comb[lf,:]
191
+ gamma[(i*R + lm), (j*R + lf)] = generate_matrix_element_ſ(i, j, exp_index1, exp_index2, C, geo_par, options)
192
+ #fin for
193
+ #fin for
194
+ #fin for
195
+ #fin for
196
+ #print("Hello from numba")
197
+ return gamma
198
+ #fin función
199
+
200
+
201
+ if __name__ == "__main__":
202
+ cc.compile()
203
+ #C_const = np.genfromtxt('constantes.csv', delimiter=',', skip_header=0, dtype=float)
204
+ #aa = generate_term_in_ſ(np.array([1, 0, 0]), np.array([0, 0, 1]), 0, 1, 0, 2, C_const, np.array([1.0, 1.0, 1.0]))
205
+ #print(aa)
206
+ #aac = generate_matrix_element_ſ(0, 1, np.array([1,0,0]), np.array([0,0,1]), C_const, np.array([1.0,1.0,1.0]))
207
+ #print(aac/8)
208
+ #print(gamma_matrix(1, C_const, np.array([1.0, 1.0, 1.0])))
@@ -0,0 +1,361 @@
1
+ #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
2
+ #include <stdio.h>
3
+ #include <stdlib.h>
4
+ #include <Python.h>
5
+ #include <numpy/arrayobject.h>
6
+ #include <math.h>
7
+ #include <omp.h>
8
+
9
+ double fact2(int N)
10
+ {
11
+ if (N < -1)
12
+ {
13
+ printf("Arithmetic Error: Cannot get double factorial of an input less than -1\n");
14
+ exit(EXIT_FAILURE);
15
+ } else if ( N == 0 || N == -1 || N == 1){
16
+ return 1.0;
17
+ } else {
18
+ double prod = 1.0;
19
+ for (int i = N; i > 1; i-=2)
20
+ {
21
+ prod = prod * i;
22
+ //printf("i: %i, prod: %li\n", i, prod);
23
+ }
24
+ return prod;
25
+ }
26
+ }
27
+
28
+ int it_c(int i, int j)
29
+ {
30
+ int index;
31
+ if (i == j) { index = i;}
32
+ else {index = 6 - (i +j);}
33
+ return index;
34
+ }
35
+
36
+ double generate_term_in_gamma_matrix_element(int exp_index1[3], int exp_index2[3], int i1, int i2, int j1, int j2, double C[6][6], double geo_par[3], int options)
37
+ {
38
+ int coeff[3], Q, S;
39
+ int array_j1[3] = {0,0,0};
40
+ int array_j2[3] = {0,0,0};
41
+ double R, alpha, beta, P;
42
+ array_j1[j1] = 1;
43
+ array_j2[j2] = 1;
44
+ for (int i = 0; i < 3; i++) {coeff[i] = exp_index1[i] + exp_index2[i] + 1 - array_j1[i] - array_j2[i];}
45
+ Q = (1 - (int)pow(-1,coeff[0]))*(1 - (int)pow(-1,coeff[1]))*(1 - (int)pow(-1,coeff[2]));
46
+ S = exp_index1[j1]*exp_index2[j2];
47
+ if (Q == 0 || S == 0)
48
+ {
49
+ return 0;
50
+ }
51
+ if (options == 1)
52
+ {
53
+ R = (0.0+fact2(coeff[0] - 2)*fact2(coeff[1] - 2))/(fact2(coeff[0] + coeff[1])*coeff[2]);
54
+ alpha = M_PI/4;
55
+ } else if (options == 2) {
56
+ R = (0.0+fact2(coeff[0] - 2)*fact2(coeff[1] - 2)*fact2(coeff[2] - 2))/fact2(coeff[0] + coeff[1] + coeff[2]);
57
+ alpha = M_PI/6;
58
+ } else {
59
+ R = 1.0/(coeff[0]*coeff[1]*coeff[2]);
60
+ alpha = 1.0;
61
+ }
62
+ if (j1 == j2) {beta = (geo_par[0]*geo_par[1]*geo_par[2])/pow(geo_par[j1],2);}
63
+ else {beta = geo_par[3-j1-j2];}
64
+ P = 4*alpha*C[it_c(i1,j1)][it_c(i2,j2)]*beta;
65
+ //for (int i = 0; i < 6; i++){ for (int j = 0; j < 6; j++) {printf("%f ", C[i][j]);} printf("\n");}
66
+ //for (int i = 0; i < 3; i++) {printf("%f, %i, %i\n", geo_par[i], exp_index1[i], exp_index2[i]); }
67
+ return P*Q*S*R;
68
+ }
69
+
70
+ double generate_gamma_matrix_element(int i1, int i2, int exp_index1[3], int exp_index2[3], double C[6][6], double geo_par[3], int options)
71
+ {
72
+ double suma = 0.0;
73
+ for (int j = 0; j < 3; j++)
74
+ {
75
+ for (int l = 0; l < 3; l++)
76
+ {
77
+ suma += generate_term_in_gamma_matrix_element(exp_index1, exp_index2, i1, i2, j, l, C, geo_par, options);
78
+ }
79
+ }
80
+ return suma;
81
+ }
82
+
83
+ double generate_E_matrix_element(int i1, int i2, int exp_index1[3], int exp_index2[3], int options)
84
+ {
85
+ if (i1 != i2)
86
+ {
87
+ return 0.0;
88
+ } else {
89
+ int coeff[3], Q;
90
+ double R;
91
+ for (int i = 0; i < 3; i++) {coeff[i] = exp_index1[i] + exp_index2[i] + 1;}
92
+ Q = (1 - (int)pow(-1,coeff[0]))*(1 - (int)pow(-1,coeff[1]))*(1 - (int)pow(-1,coeff[2]));
93
+ if (Q == 0)
94
+ {
95
+ return 0.0;
96
+ }
97
+ if (options == 1)
98
+ {
99
+ R = (0.0+fact2(coeff[0] - 2)*fact2(coeff[1] - 2))/(fact2(coeff[0] + coeff[1])*coeff[2]);
100
+ } else if (options == 2) {
101
+ R = (0.0+fact2(coeff[0] - 2)*fact2(coeff[1] - 2)*fact2(coeff[2] - 2))/(0.0+fact2(coeff[0] + coeff[1] + coeff[2]));
102
+ } else {
103
+ R = 1.0/(coeff[0]*coeff[1]*coeff[2]);
104
+ }
105
+ //for (int i = 0; i < 3; i++) {printf("coeff %i: %i, fact: %li \n", i, coeff[i], fact2(coeff[i]));}
106
+ //printf("Operacion 1: %li\n", (fact2(coeff[0] - 2)*fact2(coeff[1] - 2)));
107
+ //printf("Operacion 2: %li\n", fact2(coeff[0] + coeff[1])*coeff[2]);
108
+ //printf("35!!= %f\n", fact2(35));
109
+ return Q*R;
110
+ }
111
+ }
112
+
113
+ // Function to generate combinations
114
+ int **generate_combinations(int N) {
115
+ int R = ((N + 1) * (N + 2) * (N + 3))/6;
116
+
117
+ // Allocate memory for the combinations array
118
+ int **combi = (int **)malloc(R * sizeof(int *));
119
+ for (int i = 0; i < R; i++)
120
+ {
121
+ combi[i] = (int *)malloc(3 * sizeof(int));
122
+ }
123
+
124
+ int l = 0;
125
+ for (int n = 0; n <= N; n++) {
126
+ for (int i = 0; i <= n; i++) {
127
+ for (int j = 0; j <= n - i; j++) {
128
+ int k = n - i - j;
129
+ combi[l][0] = i;
130
+ combi[l][1] = j;
131
+ combi[l][2] = k;
132
+ l++;
133
+ }
134
+ }
135
+ }
136
+ return combi;
137
+ }
138
+
139
+
140
+ // Function to free allocated memory
141
+ void free_combinations(int **combi, int total_combinations) {
142
+ for (int i = 0; i < total_combinations; i++) {
143
+ free(combi[i]);
144
+ }
145
+ free(combi);
146
+ }
147
+
148
+ PyObject *gamma_matrix_py(PyObject *self, PyObject *args)
149
+ {
150
+ PyArrayObject *C, *geo_par;
151
+ int N, options;
152
+
153
+ if (!PyArg_ParseTuple(args, "iO!O!i", &N, &PyArray_Type, &C, &PyArray_Type, &geo_par, &options))
154
+ {
155
+ return NULL;
156
+ }
157
+
158
+ if (!PyArray_Check(C) || !PyArray_Check(geo_par))
159
+ {
160
+ PyErr_SetString(PyExc_TypeError, "Expected numpy arrays for C and geo_par");
161
+ return NULL;
162
+ }
163
+ double *geo_par_data = (double *)PyArray_DATA(geo_par);
164
+ double (*C_data)[6] = (double (*)[6])PyArray_DATA(C);
165
+
166
+ if (PyArray_NDIM(geo_par) != 1 || PyArray_DIM(geo_par, 0) != 3 ||
167
+ PyArray_NDIM(C) != 2 || PyArray_DIM(C, 0) != 6 || PyArray_DIM(C, 1) != 6)
168
+ {
169
+ PyErr_SetString(PyExc_ValueError, "C must be an np.array of dimensions (6,6) and geo_par must be an array of dimensions (3,)");
170
+ return NULL;
171
+ }
172
+ /*
173
+ * Aquí se empieza a escribir la función de verdad. Bueno casi. Aquí definimos que tan grande queremos que sea la matriz de respuesta
174
+ */
175
+ int R = ((N + 1)*(N + 2)*(N + 3))/6;
176
+ npy_intp dims[2] = {3*R, 3*R};
177
+ PyObject *gamma_array = PyArray_SimpleNew(2, dims, NPY_DOUBLE);
178
+ if (gamma_array == NULL)
179
+ {
180
+ PyErr_SetString(PyExc_MemoryError, "Gamma array memory could be not allocated");
181
+ return NULL;
182
+ }
183
+ double *gamma_data = (double *)PyArray_DATA((PyArrayObject *)gamma_array);
184
+ /*
185
+ * Ahora sí aquí empiezas a codificar la lógica de la función
186
+ */
187
+ int **combi;
188
+ combi = generate_combinations(N); //Recuerde que el primer índice de combi es la combinación específica y el segundo va de cero a 2
189
+ if (combi == NULL)
190
+ {
191
+ PyErr_SetString(PyExc_MemoryError, "Combinations array memory could not be allocated");
192
+ Py_DECREF(gamma_array); // Free gamma_array if combi allocation fails
193
+ return NULL;
194
+ }
195
+ #pragma omp parallel for collapse(4)
196
+ for (int i = 0; i < 3; i++)
197
+ {
198
+ for (int k = 0; k < 3; k++)
199
+ {
200
+ for (int lm = 0; lm < R; lm++)
201
+ {
202
+ for (int lf = 0; lf < R; lf++)
203
+ {
204
+ gamma_data[3*R*(i*R +lm) + (k*R + lf)] = generate_gamma_matrix_element(i, k, combi[lm], combi[lf], C_data, geo_par_data, options);
205
+ }
206
+ }
207
+ }
208
+ }
209
+ free_combinations(combi, R);
210
+ //printf("Hello from C \n");
211
+ return gamma_array;
212
+
213
+ }
214
+
215
+ PyObject *E_matrix_py(PyObject *self, PyObject *args)
216
+ {
217
+ int N, options;
218
+ if (!PyArg_ParseTuple(args, "ii", &N, &options))
219
+ {
220
+ return NULL;
221
+ }
222
+ int R = ((N + 1)*(N + 2)*(N + 3))/6;
223
+ int **combi;
224
+ npy_intp dims[2] = {3*R, 3*R};
225
+ PyObject *E_array = PyArray_SimpleNew(2, dims, NPY_DOUBLE);
226
+ if (E_array == NULL)
227
+ {
228
+ PyErr_SetString(PyExc_MemoryError, "Memory for E matrix could not be allocated");
229
+ return NULL;
230
+ }
231
+ double *E_data = (double *)PyArray_DATA((PyArrayObject *)E_array);
232
+ combi = generate_combinations(N);
233
+
234
+ if (combi == NULL) {
235
+ PyErr_SetString(PyExc_MemoryError, "Combinations array memory could not be allocated");
236
+ Py_DECREF(E_array);
237
+ return NULL;
238
+ }
239
+ #pragma omp parallel for collapse(4)
240
+ for (int i = 0; i < 3; i++)
241
+ {
242
+ for (int k = 0; k < 3; k++)
243
+ {
244
+ for (int lm = 0; lm < R; lm++)
245
+ {
246
+ for (int lf = 0; lf < R; lf++)
247
+ {
248
+ E_data[3*R*(i*R +lm) + (k*R + lf)] = generate_E_matrix_element(i, k, combi[lm], combi[lf], options);
249
+ }
250
+ }
251
+ }
252
+ }
253
+ free_combinations(combi, R);
254
+ return E_array;
255
+
256
+ }
257
+
258
+ PyObject *generate_element_in_gamma_matrix_py(PyObject *self, PyObject *args)
259
+ {
260
+ PyArrayObject *exp_index1, *exp_index2, *C, *geo_par;
261
+ int i1, i2, options;
262
+ double result;
263
+
264
+
265
+ if (!PyArg_ParseTuple(args, "iiO!O!O!O!i",
266
+ &i1, &i2,
267
+ &PyArray_Type, &exp_index1,
268
+ &PyArray_Type, &exp_index2,
269
+ &PyArray_Type, &C,
270
+ &PyArray_Type, &geo_par,
271
+ &options))
272
+ {
273
+ return NULL;
274
+ }
275
+ // Validate input arrays
276
+ if (!PyArray_Check(exp_index1) || !PyArray_Check(exp_index2) ||
277
+ !PyArray_Check(C) || !PyArray_Check(geo_par))
278
+ {
279
+ PyErr_SetString(PyExc_TypeError, "Expected numpy arrays for exp_index1, exp_index2, C, and geo_par.");
280
+ return NULL;
281
+ }
282
+
283
+ // Convert numpy arrays to C arrays
284
+ int *exp_index1_data = (int *)PyArray_DATA(exp_index1);
285
+ int *exp_index2_data = (int *)PyArray_DATA(exp_index2);
286
+ double (*C_data)[6] = (double (*)[6])PyArray_DATA(C);
287
+ double *geo_par_data = (double *)PyArray_DATA(geo_par);
288
+
289
+ // Ensure dimensions match expected sizes
290
+ if (PyArray_NDIM(exp_index1) != 1 || PyArray_DIM(exp_index1, 0) != 3 ||
291
+ PyArray_NDIM(exp_index2) != 1 || PyArray_DIM(exp_index2, 0) != 3 ||
292
+ PyArray_NDIM(C) != 2 || PyArray_DIM(C, 0) != 6 || PyArray_DIM(C, 1) != 6 ||
293
+ PyArray_NDIM(geo_par) != 1 || PyArray_DIM(geo_par, 0) != 3)
294
+ {
295
+ PyErr_SetString(PyExc_ValueError, "Invalid dimensions for input arrays.");
296
+ return NULL;
297
+ }
298
+ //for (int i = 0; i < 3; i++) {printf("|%i|%i|\n", exp_index1_data[i], exp_index2_data[i]);}
299
+ // Call the C function
300
+ result = generate_gamma_matrix_element(i1, i2, exp_index1_data, exp_index2_data,
301
+ C_data,
302
+ geo_par_data, options);
303
+
304
+ // Return the result as a Python float
305
+ return PyFloat_FromDouble(result);
306
+ }
307
+
308
+ PyObject *generate_element_in_E_matrix_py(PyObject *self, PyObject *args)
309
+ {
310
+ PyArrayObject *exp_index1, *exp_index2;
311
+ int i1, i2, options;
312
+ double result;
313
+ if (!PyArg_ParseTuple(args, "iiO!O!i",
314
+ &i1, &i2,
315
+ &PyArray_Type, &exp_index1,
316
+ &PyArray_Type, &exp_index2,
317
+ &options))
318
+ {
319
+ return NULL;
320
+ }
321
+ if (!PyArray_Check(exp_index1) || !PyArray_Check(exp_index2))
322
+ {
323
+ PyErr_SetString(PyExc_TypeError, "Expected numpy arrays for exp_index1 and exp_index2");
324
+ return NULL;
325
+ }
326
+
327
+ int *exp_index1_data = (int *)PyArray_DATA(exp_index1);
328
+ int *exp_index2_data = (int *)PyArray_DATA(exp_index2);
329
+ if (PyArray_NDIM(exp_index1) != 1 || PyArray_DIM(exp_index1, 0) != 3 ||
330
+ PyArray_NDIM(exp_index2) != 1 || PyArray_DIM(exp_index2, 0) != 3)
331
+ {
332
+ PyErr_SetString(PyExc_ValueError, "Invalid dimensions for exp_index1 or exp_index2. Must be (3,)");
333
+ return NULL;
334
+ }
335
+ result = generate_E_matrix_element(i1, i2, exp_index1_data, exp_index2_data, options);
336
+ return PyFloat_FromDouble(result);
337
+ }
338
+
339
+
340
+ static PyMethodDef methods[] = {
341
+ {"generate_gamma_matrix_element", generate_element_in_gamma_matrix_py, METH_VARARGS, "This functions generates a term in teh sum of an element of gamma matrix"},
342
+ {"generate_E_matrix_element", generate_element_in_E_matrix_py, METH_VARARGS, "This function generates a matrix element of E"},
343
+ {"gamma_matrix", gamma_matrix_py, METH_VARARGS, "This function computes the gamma matrix"},
344
+ {"E_matrix", E_matrix_py, METH_VARARGS, "This function computes the E matrix"},
345
+ {NULL, NULL, 0, NULL}
346
+ };
347
+
348
+ static struct PyModuleDef rus_module = {
349
+ PyModuleDef_HEAD_INIT,
350
+ "rus",
351
+ "Prototipo de modelo de rus.",
352
+ -1,
353
+ methods
354
+ };
355
+
356
+ PyMODINIT_FUNC PyInit_rus()
357
+ {
358
+ PyObject *module = PyModule_Create(&rus_module);
359
+ import_array();
360
+ return module;
361
+ }
@@ -0,0 +1,53 @@
1
+ Metadata-Version: 2.1
2
+ Name: ruspectroscopy-tools
3
+ Version: 0.0.1
4
+ Summary: A simple tool that generates Gamma matrix and E matrix, given the elastic constants, mass, dimansions and shape of a sample, in Resonant Ultrasound Spectroscopy. Use scipy.linalg.eigh(...) to solve the forward problem
5
+ Home-page: https://github.com/jacubillos10/RUSpectroscopy_Tools
6
+ Author: Alejandro Cubillos
7
+ Author-email: alejandro4cm@outlook.com
8
+ License: MIT
9
+ Classifier: Programming Language :: Python :: 3
10
+ Classifier: License :: OSI Approved :: MIT License
11
+ Classifier: Operating System :: OS Independent
12
+ Requires-Python: >=3.6
13
+ Description-Content-Type: text/markdown
14
+ License-File: LICENSE
15
+
16
+ # RUSpectroscopy_tools
17
+
18
+ A C extension module for generating Gamma and Epsilon matrices, in Resonant Ultrasound Spectroscopy.
19
+
20
+ ## Authors
21
+
22
+ - Alejandro Cubillos - alejandro4cm@outlook.com
23
+ - Manuela Rivas
24
+ - Julián Rincón
25
+
26
+ ## Import:
27
+ ```python
28
+ from rusmodules import rus
29
+ ```
30
+
31
+ ## Usage Example
32
+ ```python
33
+ gamma = rus.gamma_matrix(N, C, geometry, shape)
34
+ E = rus.E_matrix(N, shape)
35
+ ```
36
+
37
+ # Where:
38
+ * N <int> represents the maximim grade of the polynomials of the basis functions
39
+ * C <np.array> 6x6 matrix with the elastic constants
40
+ * geometry <np.array> (3,) shape array containing the dimensions of the sample
41
+ * shape <int> 0 for parallelepiped, 1 for cylinder and 2 to spheroid
42
+
43
+ Solve forward problem with scipy:
44
+ Eigenvalues return $m\omega^2$
45
+ ```python
46
+ eigenvals, eigenvects = scipy.linalg.eigh(a = gamma, b = E)
47
+ omega = (eigenvals[6:]/m)**(1/2) #remember the fisrt 6 eigenvalues must be near zero
48
+ frequencies = omega/(2*np.pi)
49
+ ```
50
+
51
+ ## Installation:
52
+ ```bash
53
+ pip3 install ruspectroscopy_tools
@@ -0,0 +1,12 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ setup.py
5
+ rusmodules/__init__.py
6
+ rusmodules/compile_data_generator.py
7
+ rusmodules/compile_rus_module.py
8
+ rusmodules/rus.c
9
+ ruspectroscopy_tools.egg-info/PKG-INFO
10
+ ruspectroscopy_tools.egg-info/SOURCES.txt
11
+ ruspectroscopy_tools.egg-info/dependency_links.txt
12
+ ruspectroscopy_tools.egg-info/top_level.txt
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,35 @@
1
+ from setuptools import setup, Extension
2
+ import numpy as np
3
+ import sys
4
+
5
+ extra_compile_flags = ["-fopenmp"]
6
+ extra_link_flags = ["-fopenmp"]
7
+
8
+ setup(
9
+ name = "ruspectroscopy_tools",
10
+ version = "0.0.1",
11
+ description = "A simple tool that generates Gamma matrix and E matrix, given the elastic constants, mass, dimansions and shape of a sample, in Resonant Ultrasound Spectroscopy. Use scipy.linalg.eigh(...) to solve the forward problem",
12
+ long_description = open("README.md").read(),
13
+ long_description_content_type = "text/markdown",
14
+ author = "Alejandro Cubillos",
15
+ author_email = "alejandro4cm@outlook.com",
16
+ url = "https://github.com/jacubillos10/RUSpectroscopy_Tools",
17
+ packages = ["rusmodules"],
18
+ ext_modules = [
19
+ Extension(
20
+ "rusmodules.rus",
21
+ sources = ["rusmodules/rus.c"],
22
+ include_dirs = [np.get_include()],
23
+ extra_compile_args = extra_compile_flags,
24
+ extra_link_args = extra_link_flags,
25
+ )
26
+ ],
27
+ classifiers = [
28
+ "Programming Language :: Python :: 3",
29
+ "License :: OSI Approved :: MIT License",
30
+ "Operating System :: OS Independent",
31
+ ],
32
+ license = "MIT",
33
+ python_requires = ">=3.6",
34
+ setup_requires = ["numpy"],
35
+ )