Coverage for src/flexfrac1d/lib/displacement.py: 96%

1 import numpy as np

2 from .constants import PI_D4, SQR2

3 from . import numerical

4

5

6 def _wavefield(x, c_amps, c_wavenumbers):

7 return np.imag(c_amps @ _unit_wavefield(x, c_wavenumbers))

8

9

10 def _unit_wavefield(x, c_wavenumbers):

11 return np.exp((1j * c_wavenumbers[:, None]) * x)

12

13

14 def _dis_par_amps(red_num: float, wave_params: tuple[np.ndarray]):

15 """Complex amplitudes of individual particular solutions"""

16 c_amplitudes, c_wavenumbers = wave_params

17 return c_amplitudes / (1 + 0.25 * (c_wavenumbers / red_num) ** 4)

18

19

20 def _dis_hom_mat(red_num: float, length: float):

21 """Linear application to determine, from the BCs, the coefficients of

22 the four independent solutions to the homo ODE"""

23 adim = red_num * length

24 adim2 = 2 * adim

25 denom = (

26 -2

27 * red_num**2

28 * (np.expm1(-adim2) ** 2 + 2 * np.exp(-adim2) * (np.cos(adim2) - 1))

29 )

30 mat = np.array(

31 [

32 [

33 SQR2 * np.exp(-adim) * np.sin(adim2 + PI_D4) - np.exp(-3 * adim),

34 -SQR2 * np.cos(adim + PI_D4)

35 - 3 * np.exp(-adim2) * np.sin(adim)

36 + np.exp(-adim2) * np.cos(adim),

37 np.exp(-adim) * np.sin(adim2) - np.exp(-adim) + np.exp(-3 * adim),

38 np.cos(adim)

39 - 2 * np.exp(-adim2) * np.sin(adim)

40 - np.exp(-adim2) * np.cos(adim),

41 ],

42 [

43 -SQR2 * np.exp(-adim) * np.cos(adim2 + PI_D4)

44 + 2 * np.exp(-adim)

45 - np.exp(-3 * adim),

46 -SQR2 * np.sin(adim + PI_D4)

47 + SQR2 * np.exp(-adim2) * np.cos(adim + PI_D4),

48 -np.exp(-adim) * np.cos(adim2) + np.exp(-adim),

49 np.sin(adim) - np.exp(-adim2) * np.sin(adim),

50 ],

51 [

52 -1 + SQR2 * np.exp(-adim2) * np.cos(adim2 + PI_D4),

53 (3 * np.sin(adim) + np.cos(adim)) * np.exp(-adim)

54 - SQR2 * np.exp(-3 * adim) * np.sin(adim + PI_D4),

55 (np.sin(adim2) + 1) * np.exp(-adim2) - 1,

56 (-2 * np.sin(adim) + np.cos(adim)) * np.exp(-adim)

57 - np.exp(-3 * adim) * np.cos(adim),

58 ],

59 [

60 (SQR2 * np.sin(adim2 + PI_D4) - 2) * np.exp(-adim2) + 1,

61 -SQR2 * np.exp(-adim) * np.sin(adim + PI_D4)

62 + SQR2 * np.exp(-3 * adim) * np.cos(adim + PI_D4),

63 (-np.cos(adim2) + 1) * np.exp(-adim2),

64 np.exp(-adim) * np.sin(adim) - np.exp(-3 * adim) * np.sin(adim),

65 ],

66 ]

67 )

68 mat[:, 2:] /= red_num

69

70 return mat / denom

71

72

73 def _dis_hom_rhs(floe_params: tuple[float], wave_params: tuple[np.ndarray]):

74 """Vector onto which apply the matrix, to extract the coefficients"""

75 red_num, length = floe_params

76 _, c_wavenumbers = wave_params

77 exp_arg = 1j * c_wavenumbers * length

78

79 r1 = c_wavenumbers**2 * _dis_par_amps(red_num, wave_params)

80 r2 = np.imag(r1 @ np.exp(exp_arg))

81 r3 = 1j * c_wavenumbers * r1

82 r4 = np.imag(r3 @ np.exp(exp_arg))

83 r1 = np.imag(r1).sum()

84 r3 = np.imag(r3).sum()

85

86 return np.array((r1, r2, r3, r4))

87

88

89 def _dis_hom_coefs(

90 floe_params: tuple[float], wave_params: tuple[np.ndarray]

91) -> np.ndarray:

92 """Coefficients of the four orthogonal homogeneous solutions"""

93 return _dis_hom_mat(*floe_params) @ _dis_hom_rhs(floe_params, wave_params)

94

95

96 def _dis_hom(x: np.ndarray, floe_params: tuple[float], wave_params: tuple[np.ndarray]):

97 """Homogeneous solution to the displacement ODE"""

98 red_num, length = floe_params

99 arr = red_num * x

100 cosx, sinx = np.cos(arr), np.sin(arr)

101 expx = np.exp(-red_num * (length - x))

102 exmx = np.exp(-arr)

103 return np.vstack(

104 ([expx * cosx, expx * sinx, exmx * cosx, exmx * sinx])

105 ).T @ _dis_hom_coefs(floe_params, wave_params)

106

107

108 def _dis_par(x: np.ndarray, red_num: float, wave_params: tuple[np.ndarray]):

109 """Sum of the particular solutions to the displacement ODE"""

110 return _wavefield(x, _dis_par_amps(red_num, wave_params), wave_params[1])

111

112

113 def displacement(

114 x,

115 floe_params: tuple[float],

116 wave_params: tuple[np.ndarray],

117 growth_params: tuple | None = None,

118 an_sol: bool | None = None,

119 num_params: dict | None = None,

120):

121 if numerical._use_an_sol(an_sol, floe_params[1], growth_params): 121 ↛ 125line 121 didn't jump to line 125, because the condition on line 121 was never false

122 return _dis_hom(x, floe_params, wave_params) + _dis_par(

123 x, floe_params[0], wave_params

124 )

125 return numerical.displacement(

126 x, floe_params, wave_params, growth_params, num_params

127 )