Coverage for tests/test_fracture.py: 0%

121 statements  

« prev     ^ index     » next       coverage.py v7.9.1, created at 2025-09-11 16:23 +0200

1import pathlib 

2from typing import Sequence 

3 

4import numpy as np 

5import pytest 

6 

7import swiift.lib.phase_shift as ps 

8import swiift.model.frac_handlers as fh 

9import swiift.model.model as model 

10from swiift.model.model import DiscreteSpectrum, Domain, Floe, Ice, Ocean 

11from tests.utils import fracture_handler_types 

12 

13scattering_handler_types = ( 

14 ps.ContinuousScatteringHandler, 

15 ps.UniformScatteringHandler, 

16 ps.PerturbationScatteringHandler, 

17) 

18 

19# Data for stability tests 

20PATH_FRACTURE_TARGETS = pathlib.Path("tests/target/fracture") 

21binary_energy_no_growth_target = np.loadtxt( 

22 PATH_FRACTURE_TARGETS.joinpath("binary_fracture.ssv") 

23) 

24binary_energy_with_growth_target = np.loadtxt( 

25 PATH_FRACTURE_TARGETS.joinpath("binary_fracture_with_growth.ssv") 

26) 

27binary_strain_no_growth_target = np.loadtxt( 

28 PATH_FRACTURE_TARGETS.joinpath("binary_strain_fracture.ssv") 

29) 

30binary_strain_with_growth_target = np.loadtxt( 

31 PATH_FRACTURE_TARGETS.joinpath("binary_strain_fracture_with_growth.ssv") 

32) 

33multi_strain_no_growth_archive = np.load( 

34 PATH_FRACTURE_TARGETS.joinpath("multi_strain_fracture.npz") 

35) 

36multi_strain_no_growth_target = ( 

37 (row, multi_strain_no_growth_archive[f"res{i:02d}"]) 

38 for i, row in enumerate(multi_strain_no_growth_archive["params"]) 

39) 

40multi_strain_with_growth_archive = np.load( 

41 PATH_FRACTURE_TARGETS.joinpath("multi_strain_fracture_with_growth.npz") 

42) 

43multi_strain_with_growth_target = ( 

44 (row, multi_strain_with_growth_archive[f"res{i:02d}"]) 

45 for i, row in enumerate(multi_strain_with_growth_archive["params"]) 

46) 

47 

48 

49def make_wuf(array: np.ndarray, growth_params: tuple | None) -> model.WavesUnderFloe: 

50 ( 

51 frac_toughness, 

52 strain_threshold, 

53 thickness, 

54 youngs_modulus, 

55 depth, 

56 gravity, 

57 amplitude, 

58 frequency, 

59 length, 

60 left_edge, 

61 phase, 

62 ) = array 

63 

64 ice = Ice( 

65 frac_toughness=frac_toughness, 

66 strain_threshold=strain_threshold, 

67 thickness=thickness, 

68 youngs_modulus=youngs_modulus, 

69 ) 

70 ocean = Ocean(depth=depth) 

71 spectrum = DiscreteSpectrum( 

72 amplitudes=amplitude, frequencies=frequency, phases=phase 

73 ) 

74 domain = Domain.from_discrete(gravity, spectrum, ocean, None, growth_params) 

75 floe = Floe(left_edge=left_edge, length=length, ice=ice) 

76 domain.add_floes(floe) 

77 return domain.subdomains[0] 

78 

79 

80def test_abstract(): 

81 # Abstract classes, should not be instantiated 

82 with pytest.raises(TypeError): 

83 fh._FractureHandler() 

84 with pytest.raises(TypeError): 

85 fh._StrainFracture() 

86 

87 

88@pytest.mark.parametrize("fracture_handler_type", fracture_handler_types) 

89@pytest.mark.parametrize("scattering_spec_type", scattering_handler_types) 

90def test_initialisation_scattering( 

91 fracture_handler_type: type[fh._FractureHandler], 

92 scattering_spec_type: type[ps._ScatteringHandler], 

93): 

94 def make_handler_from_spec(scattering_spec_type): 

95 rng_seed = 13 

96 loc, scale = 0.3, 0.005 

97 

98 match scattering_spec_type: 

99 case ps.ContinuousScatteringHandler: 

100 return scattering_spec_type() 

101 case ps.UniformScatteringHandler: 

102 return scattering_spec_type.from_seed(rng_seed) 

103 case ps.PerturbationScatteringHandler: 

104 return scattering_spec_type.from_seed(rng_seed, loc, scale) 

105 case _: # pragma: no cover 

106 raise TypeError("Unsupported scattering handler") 

107 

108 fracture_handler: fh._FractureHandler = fracture_handler_type( 

109 scattering_handler=make_handler_from_spec(scattering_spec_type) 

110 ) 

111 assert isinstance(fracture_handler, fracture_handler_type) 

112 assert isinstance(fracture_handler.scattering_handler, scattering_spec_type) 

113 

114 

115def check_no_overlap( 

116 fracture_handler: fh._FractureHandler, 

117 xf: float | Sequence[float], 

118 wuf: model.WavesUnderFloe, 

119): 

120 post_fracture_wufs = fracture_handler.split(wuf, xf) 

121 for wuf_left, wuf_right in zip(post_fracture_wufs[:-1], post_fracture_wufs[1:]): 

122 assert wuf_left.right_edge == wuf_right.left_edge 

123 

124 

125# Stability tests 

126@pytest.mark.slow 

127@pytest.mark.parametrize("row", binary_energy_no_growth_target) 

128def test_binary_energy_no_growth(row: np.ndarray): 

129 growth_params = None 

130 an_sol = True 

131 fracture_handler = fh.BinaryFracture() 

132 

133 wuf = make_wuf(row[:-1], growth_params) 

134 xf = fracture_handler.search(wuf, growth_params, an_sol, None) 

135 if xf is not None: 

136 assert np.allclose(row[-1], xf) 

137 check_no_overlap(fracture_handler, xf, wuf) 

138 else: 

139 assert np.isnan(row[-1]) 

140 

141 

142@pytest.mark.slow 

143@pytest.mark.parametrize("row", binary_energy_with_growth_target) 

144def test_binary_energy_with_growth(row: np.ndarray): 

145 fracture_handler = fh.BinaryFracture() 

146 growth_params = np.atleast_2d(row[-3]), row[-2] 

147 an_sol = None 

148 

149 wuf = make_wuf(row[:-3], growth_params) 

150 xf = fracture_handler.search(wuf, growth_params, an_sol, None) 

151 if xf is not None: 

152 assert np.allclose(row[-1], xf) 

153 check_no_overlap(fracture_handler, xf, wuf) 

154 else: 

155 assert np.isnan(row[-1]) 

156 

157 

158@pytest.mark.parametrize("row", binary_strain_no_growth_target) 

159def test_binary_strain_no_growth(row: np.ndarray): 

160 growth_params = None 

161 an_sol = True 

162 fracture_handler = fh.BinaryStrainFracture() 

163 

164 wuf = make_wuf(row[:-1], growth_params) 

165 xf = fracture_handler.search(wuf, growth_params, an_sol, None) 

166 if xf is not None: 

167 assert np.allclose(row[-1], xf) 

168 check_no_overlap(fracture_handler, xf, wuf) 

169 else: 

170 assert np.isnan(row[-1]) 

171 

172 

173@pytest.mark.parametrize("row", binary_strain_with_growth_target) 

174def test_binary_strain_with_growth(row: np.ndarray): 

175 fracture_handler = fh.BinaryStrainFracture() 

176 growth_params = np.atleast_2d(row[-3]), row[-2] 

177 an_sol = None 

178 

179 wuf = make_wuf(row[:-3], growth_params) 

180 xf = fracture_handler.search(wuf, growth_params, an_sol, None) 

181 if xf is not None: 

182 assert np.allclose(row[-1], xf) 

183 check_no_overlap(fracture_handler, xf, wuf) 

184 else: 

185 assert np.isnan(row[-1]) 

186 

187 

188@pytest.mark.parametrize("row, target", multi_strain_no_growth_target) 

189def test_multi_strain_no_growth(row: np.ndarray, target: np.ndarray): 

190 growth_params = None 

191 an_sol = True 

192 fracture_handler = fh.MultipleStrainFracture() 

193 

194 wuf = make_wuf(row, growth_params) 

195 xfs = fracture_handler.search(wuf, growth_params, an_sol, None) 

196 if xfs is not None: 

197 assert np.allclose(target, xfs) 

198 check_no_overlap(fracture_handler, xfs, wuf) 

199 else: 

200 assert np.isnan(target) 

201 

202 

203@pytest.mark.parametrize("row, target", multi_strain_with_growth_target) 

204def test_multi_strain_with_growth(row: np.ndarray, target: np.ndarray): 

205 fracture_handler = fh.MultipleStrainFracture() 

206 # xf not part of the array here, so slightly different slicing 

207 growth_params = np.atleast_2d(row[-2]), row[-1] 

208 an_sol = None 

209 

210 wuf = make_wuf(row[:-2], growth_params) 

211 xfs = fracture_handler.search(wuf, growth_params, an_sol, None) 

212 if xfs is not None: 

213 assert np.allclose(target, xfs) 

214 check_no_overlap(fracture_handler, xfs, wuf) 

215 else: 

216 assert np.isnan(target)