• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

maurergroup / dfttoolkit / 12448697655

21 Dec 2024 09:47PM UTC coverage: 24.863% (+3.1%) from 21.747%
12448697655

push

github

a7cc32
web-flow
Merge pull request #24 from maurergroup/dependabot/pip/sphinx-8.1.3

Bump sphinx from 7.4.7 to 8.1.3

773 of 3109 relevant lines covered (24.86%)

0.25 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

28.16
dfttoolkit/utils/math_utils.py
1
from copy import deepcopy
1✔
2
from typing import Union
1✔
3

4
import numpy as np
1✔
5
import numpy.typing as npt
1✔
6
import scipy
1✔
7

8

9
def get_rotation_matrix(vec_start: npt.NDArray, vec_end: npt.NDArray) -> npt.NDArray:
1✔
10
    """
11
    Given a two (unit) vectors, vec_start and vec_end, this function calculates
12
    the rotation matrix U, so that
13
    U * vec_start = vec_end.
14

15
    U the is rotation matrix that rotates vec_start to point in the direction
16
    of vec_end.
17

18
    https://math.stackexchange.com/questions/180418/calculate-rotation-matrix-to-align-vector-a-to-vector-b-in-3d/897677
19

20
    Parameters
21
    ----------
22
    vec_start, vec_end : npt.NDArray[np.float64]
23
        Two vectors that should be aligned. Both vectors must have a l2-norm of 1.
24

25
    Returns:
26
    --------
27
    R
28
        The rotation matrix U as npt.NDArray with shape (3,3)
29
    """
30
    assert np.isclose(np.linalg.norm(vec_start), 1) and np.isclose(
×
31
        np.linalg.norm(vec_end), 1
32
    ), "vec_start and vec_end must be unit vectors!"
33

34
    v = np.cross(vec_start, vec_end)
×
35
    c = np.dot(vec_start, vec_end)
×
36
    v_x = np.array([[0, -v[2], v[1]], [v[2], 0, -v[0]], [-v[1], v[0], 0]])
×
37
    R = np.eye(3) + v_x + v_x.dot(v_x) / (1 + c)
×
38

39
    return R
×
40

41

42
def get_rotation_matrix_around_axis(axis: npt.NDArray, phi: float) -> npt.NDArray:
1✔
43
    """
44
    Generates a rotation matrix around a given vector.
45

46
    Parameters
47
    ----------
48
    axis : npt.NDArray
49
        Axis around which the rotation is done.
50
    phi : float
51
        Angle of rotation around axis in radiants.
52

53
    Returns
54
    -------
55
    R : npt.NDArray
56
        Rotation matrix
57

58
    """
59
    axis_vec = np.array(axis, dtype=np.float64)
×
60
    axis_vec /= np.linalg.norm(axis_vec)
×
61

62
    eye = np.eye(3, dtype=np.float64)
×
63
    ddt = np.outer(axis_vec, axis_vec)
×
64
    skew = np.array(
×
65
        [
66
            [0, axis_vec[2], -axis_vec[1]],
67
            [-axis_vec[2], 0, axis_vec[0]],
68
            [axis_vec[1], -axis_vec[0], 0],
69
        ],
70
        dtype=np.float64,
71
    )
72

73
    R = ddt + np.cos(phi) * (eye - ddt) + np.sin(phi) * skew
×
74
    return R
×
75

76

77
def get_rotation_matrix_around_z_axis(phi: float) -> npt.NDArray:
1✔
78
    """
79
    Generates a rotation matrix around the z axis.
80

81
    Parameters
82
    ----------
83
    phi : float
84
        Angle of rotation around axis in radiants.
85

86
    Returns
87
    -------
88
    npt.NDArray
89
        Rotation matrix
90

91
    """
92
    return get_rotation_matrix_around_axis(np.array([0.0, 0.0, 1.0]), phi)
×
93

94

95
def get_mirror_matrix(normal_vector: npt.NDArray) -> npt.NDArray:
1✔
96
    """
97
    Generates a transformation matrix for mirroring through plane given by the
98
    normal vector.
99

100
    Parameters
101
    ----------
102
    normal_vector : npt.NDArray
103
        Normal vector of the mirror plane.
104

105
    Returns
106
    -------
107
    M : npt.NDArray
108
        Mirror matrix
109

110
    """
111
    n_vec = normal_vector / np.linalg.norm(normal_vector)
×
112
    eps = np.finfo(np.float64).eps
×
113
    a = n_vec[0]
×
114
    b = n_vec[1]
×
115
    c = n_vec[2]
×
116
    M = np.array(
×
117
        [
118
            [1 - 2 * a**2, -2 * a * b, -2 * a * c],
119
            [-2 * a * b, 1 - 2 * b**2, -2 * b * c],
120
            [-2 * a * c, -2 * b * c, 1 - 2 * c**2],
121
        ]
122
    )
123
    M[np.abs(M) < eps * 10] = 0
×
124
    return M
×
125

126

127
def get_angle_between_vectors(
1✔
128
    vector_1: npt.NDArray, vector_2: npt.NDArray
129
) -> npt.NDArray:
130
    """
131
    Determines angle between two vectors.
132

133
    Parameters
134
    ----------
135
    vector_1 : npt.NDArray
136
    vector_2 : npt.NDArray
137

138
    Returns
139
    -------
140
    angle : float
141
        Angle in radiants.
142

143
    """
144
    angle = (
×
145
        np.dot(vector_1, vector_2) / np.linalg.norm(vector_1) / np.linalg.norm(vector_2)
146
    )
147
    return angle
×
148

149

150
def get_fractional_coords(
1✔
151
    cartesian_coords: npt.NDArray, lattice_vectors: npt.NDArray
152
) -> npt.NDArray:
153
    """
154
    Transform cartesian coordinates into fractional coordinates.
155

156
    Parameters
157
    ----------
158
    cartesian_coords: [N x N_dim] numpy array
159
        Cartesian coordinates of atoms (can be Nx2 or Nx3)
160
    lattice_vectors: [N_dim x N_dim] numpy array:
161
        Matrix of lattice vectors: Each ROW corresponds to one lattice vector!
162

163
    Returns
164
    -------
165
    fractional_coords: [N x N_dim] numpy array
166
        Fractional coordinates of atoms
167

168
    """
169
    fractional_coords = np.linalg.solve(lattice_vectors.T, cartesian_coords.T)
1✔
170
    return fractional_coords.T
1✔
171

172

173
def get_cartesian_coords(
1✔
174
    frac_coords: npt.NDArray, lattice_vectors: npt.NDArray
175
) -> npt.NDArray:
176
    """
177
    Transform fractional coordinates into cartesian coordinates.
178

179
    Parameters
180
    ----------
181
    frac_coords: [N x N_dim] numpy array
182
        Fractional coordinates of atoms (can be Nx2 or Nx3)
183
    lattice_vectors: [N_dim x N_dim] numpy array:
184
        Matrix of lattice vectors: Each ROW corresponds to one lattice vector!
185

186
    Returns
187
    -------
188
    cartesian_coords: [N x N_dim] numpy array
189
        Cartesian coordinates of atoms
190

191
    """
192
    return np.dot(frac_coords, lattice_vectors)
1✔
193

194

195
def get_cross_correlation_function(
1✔
196
    signal_0: npt.NDArray,
197
    signal_1: npt.NDArray,
198
    detrend: bool = False,
199
) -> npt.NDArray:
200
    """
201
    Calculate the autocorrelation function for a given signal.
202

203
    Parameters
204
    ----------
205
    signal_0 : 1D npt.NDArray
206
        First siganl for which the correlation function should be calculated.
207
    signal_1 : 1D npt.NDArray
208
        Second siganl for which the correlation function should be calculated.
209

210
    Returns
211
    -------
212
    correlation : npt.NDArray
213
        Autocorrelation function from 0 to max_lag.
214

215
    """
216
    if detrend:
×
217
        signal_0 = scipy.signal.detrend(signal_0)
×
218
        signal_1 = scipy.signal.detrend(signal_1)
×
219

220
    # cross_correlation = np.correlate(signal_0, signal_1, mode='same')
221
    cross_correlation = np.correlate(signal_0, signal_1, mode="full")
×
222
    cross_correlation = cross_correlation[cross_correlation.size // 2 :]
×
223

224
    # normalize by number of overlapping data points
225
    cross_correlation /= np.arange(cross_correlation.size, 0, -1)
×
226
    cutoff = int(cross_correlation.size * 0.75)
×
227
    cross_correlation = cross_correlation[:cutoff]
×
228

229
    return cross_correlation
×
230

231

232
def get_autocorrelation_function_manual_lag(
1✔
233
    signal: npt.NDArray, max_lag: int
234
) -> npt.NDArray:
235
    """
236
    Alternative method to determine the autocorrelation function for a given
237
    signal that used numpy.corrcoef. This function allows to set the lag
238
    manually.
239

240
    Parameters
241
    ----------
242
    signal : 1D npt.NDArray
243
        Siganl for which the autocorrelation function should be calculated.
244
    max_lag : Union[None, int]
245
        Autocorrelation will be calculated for a range of 0 to max_lag,
246
        where max_lag is the largest lag for the calculation of the
247
        autocorrelation function
248

249
    Returns
250
    -------
251
    autocorrelation : npt.NDArray
252
        Autocorrelation function from 0 to max_lag.
253

254
    """
255
    lag = npt.NDArray(range(max_lag))
×
256

257
    autocorrelation = np.array([np.nan] * max_lag)
×
258

259
    for l in lag:
×
260
        if l == 0:
×
261
            corr = 1.0
×
262
        else:
263
            corr = np.corrcoef(signal[l:], signal[:-l])[0][1]
×
264

265
        autocorrelation[l] = corr
×
266

267
    return autocorrelation
×
268

269

270
def get_fourier_transform(signal: npt.NDArray, time_step: float) -> tuple:
1✔
271
    """
272
    Calculate the fourier transform of a given siganl.
273

274
    Parameters
275
    ----------
276
    signal : 1D npt.NDArray
277
        Siganl for which the autocorrelation function should be calculated.
278
    time_step : float
279
        Time step of the signal in seconds.
280

281
    Returns
282
    -------
283
    (npt.NDArray, npt.NDArray)
284
        Frequencs and absolute values of the fourier transform.
285

286
    """
287
    # d = len(signal) * time_step
288

289
    f = scipy.fft.fftfreq(signal.size, d=time_step)
×
290
    y = scipy.fft.fft(signal)
×
291

292
    L = f >= 0
×
293

294
    return f[L], y[L]
×
295

296

297
def lorentzian(
1✔
298
    x: Union[float, npt.NDArray], a: float, b: float, c: float
299
) -> Union[float, npt.NDArray]:
300
    """
301
    Returns a Lorentzian function.
302

303
    Parameters
304
    ----------
305
    x : Union[float, npt.NDArray]
306
        Argument x of f(x) --> y.
307
    a : float
308
        Maximum of Lorentzian.
309
    b : float
310
        Width of Lorentzian.
311
    c : float
312
        Magnitude of Lorentzian.
313

314
    Returns
315
    -------
316
    f : Union[float, npt.NDArray]
317
        Outupt of a Lorentzian function.
318

319
    """
320
    # f = c / (np.pi * b * (1.0 + ((x - a) / b) ** 2))  # +d
321
    f = c / (1.0 + ((x - a) / (b / 2.0)) ** 2)  # +d
×
322

323
    return f
×
324

325

326
def gaussian_window(N, std=0.4):
1✔
327
    """
328
    Generate a Gaussian window.
329

330
    Parameters
331
    ----------
332
    N : int
333
        Number of points in the window.
334
    std : float
335
        Standard deviation of the Gaussian window, normalized
336
        such that the maximum value occurs at the center of the window.
337

338
    Returns
339
    -------
340
    window : np.array
341
        Gaussian window of length N.
342

343
    """
344
    n = np.linspace(-1, 1, N)
×
345
    window = np.exp(-0.5 * (n / std) ** 2)
×
346
    return window
×
347

348

349
def apply_gaussian_window(data, std=0.4):
1✔
350
    """
351
    Apply a Gaussian window to an array.
352

353
    Parameters
354
    ----------
355
    data : np.array
356
        Input data array to be windowed.
357
    std : float
358
        Standard deviation of the Gaussian window.
359

360
    Returns
361
    -------
362
    windowed_data : np.array
363
        Windowed data array.
364

365
    """
366
    N = len(data)
×
367
    window = gaussian_window(N, std)
×
368
    windowed_data = data * window
×
369
    return windowed_data
×
370

371

372
def hann_window(N):
1✔
373
    """
374
    Generate a Hann window.
375

376
    Parameters
377
    ----------
378
    N : int
379
        Number of points in the window.
380

381
    Returns
382
    -------
383
    np.ndarray
384
        Hann window of length N.
385
    """
386
    return 0.5 * (1 - np.cos(2 * np.pi * np.arange(N) / (N - 1)))
×
387

388

389
def apply_hann_window(data):
1✔
390
    """
391
    Apply a Hann window to an array.
392

393
    Parameters
394
    ----------
395
    data : np.ndarray
396
        Input data array to be windowed.
397

398
    Returns
399
    -------
400
    np.ndarray
401
        Windowed data array.
402
    """
403
    N = len(data)
×
404
    window = hann_window(N)
×
405
    windowed_data = data * window
×
406
    return windowed_data
×
407

408

409
def norm_matrix_by_dagonal(matrix: npt.NDArray) -> npt.NDArray:
1✔
410
    """
411
    Norms a matrix such that the diagonal becomes 1.
412

413
    | a_11 a_12 a_13 |       |   1   a'_12 a'_13 |
414
    | a_21 a_22 a_23 |  -->  | a'_21   1   a'_23 |
415
    | a_31 a_32 a_33 |       | a'_31 a'_32   1   |
416

417
    Parameters
418
    ----------
419
    matrix : npt.NDArray
420
        Matrix that should be normed.
421

422
    Returns
423
    -------
424
    matrix : npt.NDArray
425
        Normed matrix.
426

427
    """
428
    diagonal = np.array(np.diagonal(matrix))
×
429
    L = diagonal == 0.0
×
430
    diagonal[L] = 1.0
×
431

432
    new_matrix = deepcopy(matrix)
×
433
    new_matrix /= np.sqrt(
×
434
        np.tile(diagonal, (matrix.shape[1], 1)).T
435
        * np.tile(diagonal, (matrix.shape[0], 1))
436
    )
437

438
    return new_matrix
×
439

440

441
def mae(delta: np.ndarray) -> np.floating:
1✔
442
    """
443
    Calculated the mean absolute error from a list of value differnces.
444

445
    Parameters
446
    ----------
447
    delta : np.ndarray
448
        Array containing differences
449

450
    Returns
451
    -------
452
    float
453
        mean absolute error
454

455
    """
456
    return np.mean(np.abs(delta))
×
457

458

459
def rel_mae(delta: np.ndarray, target_val: np.ndarray) -> np.floating:
1✔
460
    """
461
    Calculated the relative mean absolute error from a list of value differnces,
462
    given the target values.
463

464
    Parameters
465
    ----------
466
    delta : np.ndarray
467
        Array containing differences
468
    target_val : np.ndarray
469
        Array of target values against which the difference should be compared
470

471
    Returns
472
    -------
473
    float
474
        relative mean absolute error
475

476
    """
477
    target_norm = np.mean(np.abs(target_val))
×
478
    return np.mean(np.abs(delta)).item() / (target_norm + 1e-9)
×
479

480

481
def rmse(delta: np.ndarray) -> float:
1✔
482
    """
483
    Calculated the root mean sqare error from a list of value differnces.
484

485
    Parameters
486
    ----------
487
    delta : np.ndarray
488
        Array containing differences
489

490
    Returns
491
    -------
492
    float
493
        root mean square error
494

495
    """
496
    return np.sqrt(np.mean(np.square(delta)))
×
497

498

499
def rel_rmse(delta: np.ndarray, target_val: np.ndarray) -> float:
1✔
500
    """
501
    Calculated the relative root mean sqare error from a list of value differnces,
502
    given the target values.
503

504
    Parameters
505
    ----------
506
    delta : np.ndarray
507
        Array containing differences
508
    target_val : np.ndarray
509
        Array of target values against which the difference should be compared
510

511
    Returns
512
    -------
513
    float
514
        relative root mean sqare error
515

516
    """
517
    target_norm = np.sqrt(np.mean(np.square(target_val)))
×
518
    return np.sqrt(np.mean(np.square(delta))) / (target_norm + 1e-9)
×
519

520

521
def get_moving_average(signal: npt.NDArray[np.float64], window_size: int):
1✔
522
    """
523
    Cacluated the moving average and the variance around the moving average.
524

525
    Parameters
526
    ----------
527
    signal : npt.NDArray[np.float64]
528
        Signal for which the moving average should be calculated.
529
    window_size : int
530
        Window size for the mocing average.
531

532
    Returns
533
    -------
534
    moving_avg : TYPE
535
        Moving average.
536
    variance : TYPE
537
        Variance around the moving average.
538

539
    """
540
    moving_avg = np.convolve(signal, np.ones(window_size) / window_size, mode="valid")
×
541
    variance = np.array(
×
542
        [
543
            np.var(signal[i : i + window_size])
544
            for i in range(len(signal) - window_size + 1)
545
        ]
546
    )
547

548
    return moving_avg, variance
×
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc