Coverage for tests/test_models.py: 100%
473 statements
« prev ^ index » next coverage.py v7.10.5, created at 2025-08-28 09:13 +0000
« prev ^ index » next coverage.py v7.10.5, created at 2025-08-28 09:13 +0000
1"""Test module for the functions in the `models.py` module.
3This module contains unit tests for the functions implemented in the `models.py` module. The purpose of these tests is to
4ensure the correct functionality of each function in different scenarios and to validate that the expected outputs are
5returned.
7Tests should cover various edge cases, valid inputs, and any other conditions that are necessary to confirm the
8robustness of the functions."""
10import numpy as np
11import pytest
13from app.models import (
14 BTDModel,
15 BTDModelTRMC,
16 BTDModelTRPL,
17 BTD_KWARGS,
18 BTModel,
19 BTModelTRMC,
20 BTModelTRPL,
21 BT_KWARGS,
22 Model,
23)
24from app.utility.data import are_close
26# Time array
27T = np.linspace(0, 100, 101)
30def assert_fit(
31 fit: dict,
32 popt_expected: dict,
33 contribution_expected: dict,
34 cod_expected: float,
35) -> None:
36 """Check the result of a fit.
37 :param fit: fit result
38 :param popt_expected: expected optimised fit popt
39 :param contribution_expected: expected contributions
40 :param cod_expected: expected cod"""
42 assert are_close(fit["popts"][0], popt_expected, rtol=0.05)
43 assert are_close(fit["contributions"], contribution_expected, rtol=0.025)
44 assert are_close(fit["cod"], cod_expected, rtol=0.01)
47class TestModel:
49 @pytest.fixture
50 def model(self) -> Model:
51 """Example Model object"""
53 param_ids = ["k_B", "k_T", "k_A", "y_0"]
54 units = {"k_B": "cm^3/ns", "k_T": "1/ns", "k_A": "cm^6/ns"}
55 units_html = {"k_B": "cm<sup>3</sup>/ns", "k_T": "1/ns", "k_A": "cm<sup>6</sup>/ns"}
56 factors = {"k_B": 1e-20, "k_T": 1e-3, "k_A": 1e-40}
57 fvalues = {"k_B": 1e-18, "k_T": None, "k_A": None}
58 gvalues = {"k_B": 1e-19, "k_T": 1e-3, "k_A": 1e-40}
59 gvalues_range = {"k_B": [1e-20, 1e-19], "k_T": [1e-3, 1e-2], "k_A": [1e-40]}
60 n_keys = ["n"]
61 conc_ca_ids = ["n"]
62 param_filters = []
64 def n_init(N_0) -> dict[str, float]: # pragma: no cover
65 """Return the photoexcited carrier concentration"""
66 return {"n": N_0}
68 return Model(
69 param_ids,
70 units,
71 units_html,
72 factors,
73 fvalues,
74 gvalues,
75 gvalues_range,
76 n_keys,
77 n_init,
78 conc_ca_ids,
79 param_filters,
80 )
82 def test_initialisation(self, model) -> None:
84 assert model.param_ids == ["k_B", "k_T", "k_A", "y_0"]
85 assert model.units["k_B"] == "cm^3/ns"
86 assert model.n_keys == ["n"]
87 assert model.fvalues["k_B"] == 1e-18
89 def test_get_parameter_label(self, model) -> None:
91 assert model.get_parameter_label("k_B") == "k_B (cm^3/ns)"
92 assert model.get_parameter_label("y_0") == "y_0"
94 def test_fixed_values(self, model) -> None:
96 assert model.fixed_values == {"k_B": 1e-18, "y_0": 0.0}
98 def test_eq(self, model) -> None:
100 model2 = model
101 assert model == model2
103 model3 = Model(
104 model.param_ids,
105 model.units,
106 model.units_html,
107 model.factors,
108 {"k_B": 1},
109 model.gvalues,
110 model.gvalues_range,
111 model.n_keys,
112 model.n_init,
113 model.conc_ca_ids,
114 model.param_filters,
115 )
116 assert model != model3
118 def test_get_rec_string(self, model) -> None:
120 expected = "This fit predicts low Auger. The values associated with this process may be inaccurate."
121 expected2 = (
122 "This fit predicts low Auger. The values associated with this process may be inaccurate.\nIt is "
123 "recommended to measure your sample under higher excitation fluence for this process to become significant"
124 )
125 assert model.get_contribution_recommendation("Auger") == expected
126 assert model.get_contribution_recommendation("Auger", "higher") == expected2
128 def test_rate_equations(self, model) -> None:
130 assert model._rate_equations() == {}
132 def test_calculate_contributions(self, model) -> None:
134 assert model.calculate_contributions() == {}
136 def test_calculate_fit_quantity(self, model) -> None:
138 assert model.calculate_fit_quantity() == np.array([0])
140 def test_get_contribution_recommendations(self, model) -> None:
142 assert model.get_contribution_recommendations() == [""]
145class TestBTModel:
147 @pytest.fixture
148 def model(self) -> BTModel:
149 """Example BTModel object"""
151 return BTModel(["k_T", "k_B", "k_A", "mu", "y_0"])
153 def test_initialization(self, model) -> None:
155 assert model.param_ids == ["k_T", "k_B", "k_A", "mu", "y_0"]
156 assert model.units["k_B"] == "cm3/ns"
157 assert model.factors["k_T"] == 1e-3
159 def test_rate_equations(self, model) -> None:
161 rates = model._rate_equations(n=1e17, **BT_KWARGS)
162 assert np.isclose(rates["n"], -6000100000000000.0)
164 def test_calculate_concentrations(self, model) -> None:
166 # Single pulse
167 output = model._calculate_concentrations(T, 1e17, **BT_KWARGS)
168 assert np.allclose(output["n"][0][:3], np.array([1.00000000e17, 9.43127550e16, 8.91893621e16]))
170 # Multiple pulses
171 output = model._calculate_concentrations(T, 1e17, **BT_KWARGS, p=1000)
172 assert np.allclose(output["n"][-1][:3], np.array([1.09021346e17, 1.02383279e17, 9.64515430e16]))
173 assert len(output["n"]) == 5
175 # Multiple pulses - No stabilisation
176 with pytest.raises(AssertionError):
177 model._calculate_concentrations(T, 1e17, **BT_KWARGS, p=3)
179 def test_get_carrier_concentrations(self, model) -> None:
181 popts = [{"I": 1.0, "N_0": 1e17, **BT_KWARGS}]
183 # Period provided
184 output = model.get_carrier_concentrations([T], popts, 100)
185 assert np.allclose(output[2][0]["n"][:3], np.array([1.00000000e17, 9.70802526e16, 9.43127550e16]))
186 assert np.allclose(output[0][0][:3], np.array([0.0, 0.00498008, 0.00996016]))
188 # Period not provided
189 output2 = model.get_carrier_concentrations([T], popts, 0)
190 assert np.allclose(output2[2][0]["n"][:3], np.array([1.00000000e17, 9.43127550e16, 8.91893621e16]))
191 assert np.allclose(output2[0][0][:3], np.array([0.0, 1.0, 2.0]))
193 def test_get_contribution_recommendations(self, model) -> None:
195 contributions = {"T": np.array([5.0]), "B": np.array([5.0]), "A": np.array([0])}
196 recs = model.get_contribution_recommendations(contributions)
197 expected = [
198 "This fit predicts low trapping. The values associated with this process may be inaccurate.\nIt is "
199 "recommended to measure your sample under lower excitation fluence for this process to become significant",
200 "This fit predicts low bimolecular. The values associated with this process may be inaccurate.\nIt is "
201 "recommended to measure your sample under higher excitation fluence for this process to become significant",
202 ]
203 assert recs == expected
205 model.fvalues["k_A"] = None
206 recs = model.get_contribution_recommendations(contributions)
207 expected = [
208 "This fit predicts low trapping. The values associated with this process may be inaccurate.\nIt is "
209 "recommended to measure your sample under lower excitation fluence for this process to become significant",
210 "This fit predicts low bimolecular. The values associated with this process may be inaccurate.\nIt is "
211 "recommended to measure your sample under higher excitation fluence for this process to become significant",
212 "This fit predicts low Auger. The values associated with this process may be inaccurate.\nIt is "
213 "recommended to measure your sample under higher excitation fluence for this process to become significant",
214 ]
215 assert recs == expected
217 def test_calculate_trpl(self, model) -> None:
219 result = model.calculate_trpl(T, N_0=1e17, **BT_KWARGS)
220 assert np.allclose(result[:3], np.array([1.0, 0.88948958, 0.79547423]))
222 def test_calculate_trmc(self, model) -> None:
224 result = model.calculate_trmc(T, N_0=1e17, **BT_KWARGS)
225 assert np.allclose(result[:3], np.array([20.0, 18.86255101, 17.83787242]))
228class TestBTModelTRPL:
230 def test_calculate_fit_quantity(self) -> None:
232 result = BTModelTRPL().calculate_fit_quantity(T, N_0=1e17, **BT_KWARGS)
233 assert np.allclose(result[:3], np.array([1.0, 0.88948958, 0.79547423]))
235 def test_calculate_contributions(self) -> None:
237 concentrations = BTModelTRPL()._calculate_concentrations(T, 1e16, **BT_KWARGS)
238 concentrations = {key: value[0] for key, value in concentrations.items()}
239 contributions = BTModelTRPL().calculate_contributions(T, **concentrations, **BT_KWARGS)
240 expected = {
241 "T": np.float64(74.27571672613342),
242 "B": np.float64(25.724244681350395),
243 "A": np.float64(3.859251618686695e-05),
244 }
245 assert are_close(contributions, expected)
247 def test_get_carrier_accumulation(self) -> None:
249 N0s = [1e17, 1e18]
250 popts = [{"N_0": n, **BT_KWARGS} for n in N0s]
252 # 100 ns period
253 output = BTModelTRPL().get_carrier_accumulation(popts, 100)
254 ca_expected = [np.float64(2.1474605461112906), np.float64(0.3260927825203319)]
255 decay_expected = [1.0, 9.99896859e-01, 9.99896716e-01]
256 assert are_close(output["CA"], ca_expected)
257 assert are_close(output["Pulse S"][-1][:3], decay_expected)
259 # 50 ns period
260 output = BTModelTRPL().get_carrier_accumulation(popts, 50)
261 ca_expected = [np.float64(4.931657664085842), np.float64(0.8414419278757801)]
262 decay_expected = [1.0, 0.99989505, 0.99989491]
263 assert are_close(output["CA"], ca_expected)
264 assert are_close(output["Pulse S"][-1][:3], decay_expected)
266 def test_generate_decays(self) -> None:
268 # Without noise
269 xs_data, ys_data, N0s = BTModelTRPL().generate_decays()
270 assert are_close(ys_data[0][:3], [1.0, 0.99476408, 0.98955622])
271 assert are_close(ys_data[-1][:3], [1.0, 0.9706254, 0.94245754])
273 # With noise
274 xs_data, ys_data, N0s = BTModelTRPL().generate_decays(noise=0.02)
275 assert are_close(ys_data[0][:3], [0.96980343, 1.0, 0.98891807])
276 assert are_close(ys_data[-1][:3], [1.0, 0.95023039, 0.95215302])
278 def test_fit(self) -> None:
280 # -------------------------------------------------- NO NOISE --------------------------------------------------
282 test_data = BTModelTRPL().generate_decays()
283 fit = BTModelTRPL().fit(*test_data)
284 popt_expected = {
285 "k_T": np.float64(0.009999986902280364),
286 "k_B": np.float64(5.000059540779067e-19),
287 "k_A": 0.0,
288 "y_0": 0.0,
289 "I": 1.0,
290 "N_0": 1000000000000000.0,
291 }
292 contribution_expected = {
293 "T": np.array([96.76851866, 75.55601245, 25.64917742]),
294 "B": np.array([3.23148134, 24.44398755, 74.35082258]),
295 "A": np.array([0.0, 0.0, 0.0]),
296 }
297 assert_fit(fit, popt_expected, contribution_expected, 0.9999999999984733)
299 # ---------------------------------------------------- NOISY ---------------------------------------------------
301 test_data = BTModelTRPL().generate_decays(0.05)
302 fit = BTModelTRPL().fit(*test_data)
303 popt_expected = {
304 "k_T": np.float64(0.010968908554455715),
305 "k_B": np.float64(5.169626461951155e-19),
306 "k_A": 0.0,
307 "y_0": 0.0,
308 "I": 1.0,
309 "N_0": 1000000000000000.0,
310 }
311 contribution_expected = {
312 "T": np.array([96.95417309, 76.62826407, 26.73269065]),
313 "B": np.array([3.04582691, 23.37173593, 73.26730935]),
314 "A": np.array([0.0, 0.0, 0.0]),
315 }
316 assert_fit(fit, popt_expected, contribution_expected, 0.9419815552329127)
318 # --------------------------------------------- NOISY / NON-FIXED I --------------------------------------------
320 test_data = BTModelTRPL().generate_decays(0.05)
321 model = BTModelTRPL()
322 model.fvalues["I"] = None
323 fit = model.fit(*test_data)
325 popt_expected = {
326 "k_T": np.float64(0.010076397709080696),
327 "k_B": np.float64(4.901882357446167e-19),
328 "I": np.float64(0.9320239638447994),
329 "k_A": 0.0,
330 "y_0": 0.0,
331 "N_0": 1000000000000000.0,
332 }
333 contribution_expected = {
334 "T": np.array([96.85353317, 76.04748899, 26.1462359]),
335 "B": np.array([3.14646683, 23.95251101, 73.8537641]),
336 "A": np.array([0.0, 0.0, 0.0]),
337 }
338 assert_fit(fit, popt_expected, contribution_expected, 0.9464096079674329)
340 # ------------------------------------------- NO NOISE / AUGER GUESS -------------------------------------------
342 test_data = BTModelTRPL().generate_decays()
343 model = BTModelTRPL()
344 model.fvalues["k_A"] = None
345 fit = model.fit(*test_data)
346 popt_expected = {
347 "k_T": np.float64(0.010000000000022824),
348 "k_B": np.float64(4.999999999757379e-19),
349 "k_A": np.float64(1.0000045458996652e-40),
350 "y_0": 0.0,
351 "I": 1.0,
352 "N_0": 1000000000000000.0,
353 }
354 contribution_expected = {
355 "T": np.array([96.76855947, 75.55622408, 25.64918984]),
356 "B": np.array([3.23144005, 24.44373996, 74.34977381]),
357 "A": np.array([4.83765434e-07, 3.59589010e-05, 1.03635229e-03]),
358 }
359 assert_fit(fit, popt_expected, contribution_expected, 1.0)
361 def test_grid_fitting(self) -> None:
363 # -------------------------------------------------- NO NOISE --------------------------------------------------
365 xs_data, ys_data, N0s = BTModelTRPL().generate_decays()
366 analysis = BTModelTRPL().grid_fitting(None, N0s, xs_data=xs_data, ys_data=ys_data)
367 popt_expected = {
368 "k_B": np.float64(5.000059541427541e-19),
369 "k_T": np.float64(0.009999986899992526),
370 "k_A": 0.0,
371 "y_0": 0.0,
372 "I": 1.0,
373 "N_0": 1000000000000000.0,
374 }
375 contributions_expected = {
376 "T": np.array([96.76851866, 75.55601244, 25.64917742]),
377 "B": np.array([3.23148134, 24.44398756, 74.35082258]),
378 "A": np.array([0.0, 0.0, 0.0]),
379 }
380 assert_fit(analysis[0], popt_expected, contributions_expected, 0.9999999999984733)
381 assert_fit(analysis[-1], popt_expected, contributions_expected, 0.9999999999984733)
383 # ---------------------------------------------------- NOISY ---------------------------------------------------
385 xs_data, ys_data, N0s = BTModelTRPL().generate_decays(0.05)
386 analysis = BTModelTRPL().grid_fitting(None, N0s, xs_data=xs_data, ys_data=ys_data)
387 popt_expected = {
388 "k_B": np.float64(5.169646462205936e-19),
389 "k_T": np.float64(0.010968900882386862),
390 "k_A": 0.0,
391 "y_0": 0.0,
392 "I": 1.0,
393 "N_0": 1000000000000000.0,
394 }
395 contributions_expected = {
396 "T": np.array([96.95415961, 76.62818434, 26.73260624]),
397 "B": np.array([3.04584039, 23.37181566, 73.26739376]),
398 "A": np.array([0.0, 0.0, 0.0]),
399 }
400 assert_fit(analysis[0], popt_expected, contributions_expected, 0.9419815552335047)
401 assert_fit(analysis[-1], popt_expected, contributions_expected, 0.9419815552345455)
403 # -------------------------------------------- NO NOISE/ NON-FIXED I -------------------------------------------
405 xs_data, ys_data, N0s = BTModelTRPL().generate_decays(0.05)
406 model = BTModelTRPL()
407 model.fvalues["I"] = None
408 analysis = model.grid_fitting(None, N0s, xs_data=xs_data, ys_data=ys_data)
409 popt_expected = {
410 "k_B": np.float64(4.901881281164643e-19),
411 "k_T": np.float64(0.0100763981602312),
412 "I": np.float64(0.9320239815477664),
413 "k_A": 0.0,
414 "y_0": 0.0,
415 "N_0": 1000000000000000.0,
416 }
417 contributions_expected = {
418 "T": np.array([96.85353398, 76.04749368, 26.14624072]),
419 "B": np.array([3.14646602, 23.95250632, 73.85375928]),
420 "A": np.array([0.0, 0.0, 0.0]),
421 }
422 assert_fit(analysis[0], popt_expected, contributions_expected, 0.9464096079674144)
423 assert_fit(analysis[-1], popt_expected, contributions_expected, 0.9464096079674144)
425 # ------------------------------------------- NO NOISE / AUGER GUESS -------------------------------------------
427 xs_data, ys_data, N0s = BTModelTRPL().generate_decays()
428 model = BTModelTRPL()
429 model.fvalues["k_A"] = None
430 analysis = model.grid_fitting(None, N0s, xs_data=xs_data, ys_data=ys_data)
431 popt_expected = {
432 "k_B": np.float64(4.999999999517231e-19),
433 "k_T": np.float64(0.010000000000056396),
434 "k_A": np.float64(9.99999839030493e-41),
435 "y_0": 0.0,
436 "I": 1.0,
437 "N_0": 1000000000000000.0,
438 }
439 popt_expected_2 = {
440 "k_B": np.float64(3.661606705835427e-19),
441 "k_T": np.float64(0.010497516613971638),
442 "k_A": np.float64(1.7842548483988976e-36),
443 "y_0": 0.0,
444 "I": 1.0,
445 "N_0": 1000000000000000.0,
446 }
447 contributions_expected = {
448 "T": np.array([96.76855947, 75.55622408, 25.64918984]),
449 "B": np.array([3.23144005, 24.44373996, 74.34977381]),
450 "A": np.array([4.83763157e-07, 3.59587318e-05, 1.03634741e-03]),
451 }
452 contribution_expected_2 = {
453 "T": np.array([97.7152958, 80.94098053, 27.43533307]),
454 "B": np.array([2.27639629, 18.39690108, 54.37465037]),
455 "A": np.array([8.30790588e-03, 6.62118386e-01, 1.81900166e01]),
456 }
457 assert_fit(analysis[0], popt_expected, contributions_expected, 1.0)
458 assert_fit(analysis[-1], popt_expected_2, contribution_expected_2, 0.9992469395390701)
460 # ------------------------------------------------- FAILED FIT -------------------------------------------------
462 xs_data, ys_data, N0s = BTModelTRPL().generate_decays()
463 model = BTModelTRPL()
464 model.gvalues_range["k_B"] = [1e-20, -1e-20]
465 analysis = model.grid_fitting(None, N0s, xs_data=xs_data, ys_data=ys_data)
466 assert len(analysis) == 2
469class TestBTModelTRMC:
471 def test_calculate_fit_quantity(self) -> None:
473 result = BTModelTRMC().calculate_trmc(T, N_0=1e17, **BT_KWARGS)
474 assert np.allclose(result[:3], np.array([20.0, 18.86255101, 17.83787242]))
476 def test_calculate_contributions(self) -> None:
478 concentrations = BTModelTRMC()._calculate_concentrations(T, 1e16, **BT_KWARGS)
479 concentrations = {key: value[0] for key, value in concentrations.items()}
480 contributions = BTModelTRMC().calculate_contributions(T, **concentrations, **BT_KWARGS)
481 expected = {
482 "T": np.float64(76.23900060688833),
483 "B": np.float64(23.760966476140023),
484 "A": np.float64(3.291697165313421e-05),
485 }
486 assert are_close(contributions, expected)
488 def test_get_carrier_accumulation(self) -> None:
490 N0s = [1e17, 1e18]
491 popts = [{"N_0": n, **BT_KWARGS} for n in N0s]
493 # 100 ns period
494 output = BTModelTRMC().get_carrier_accumulation(popts, 100)
495 ca_expected = [np.float64(1.8119795431245034), np.float64(0.2751318097141131)]
496 decay_expected = [20.22572435, 20.22468127, 20.22467983]
497 assert are_close(output["CA"], ca_expected)
498 assert are_close(output["Pulse S"][-1][:3], decay_expected)
500 # 50 ns period
501 output = BTModelTRMC().get_carrier_accumulation(popts, 50)
502 ca_expected = [np.float64(4.161872397435568), np.float64(0.7099465316118103)]
503 decay_expected = [20.58756773, 20.58648736, 20.58648594]
504 assert are_close(output["CA"], ca_expected)
505 assert are_close(output["Pulse S"][-1][:3], decay_expected)
507 def test_generate_decays(self) -> None:
509 # Without noise
510 xs_data, ys_data, N0s = BTModelTRMC().generate_decays()
511 assert are_close(ys_data[0][:3], [20.0, 19.79115011, 19.58458361])
512 assert are_close(ys_data[-1][:3], [20.0, 18.86255101, 17.83787242])
514 # With noise
515 xs_data, ys_data, N0s = BTModelTRMC().generate_decays(noise=0.02)
516 assert are_close(ys_data[0][:3], [19.57021361, 20.07543598, 19.74939803])
517 assert are_close(ys_data[-1][:3], [20.14851545, 18.5957746, 18.1731914])
519 def test_fit(self) -> None:
521 # -------------------------------------------------- NO NOISE --------------------------------------------------
523 test_data = BTModelTRMC().generate_decays()
524 fit = BTModelTRMC().fit(*test_data)
525 popt_expected = {
526 "k_T": 0.009999990976444148,
527 "k_B": 5.000049109826438e-19,
528 "mu": 9.999999708991059,
529 "k_A": 0.0,
530 "y_0": 0.0,
531 "N_0": 1000000000000000.0,
532 }
533 contribution_expected = {
534 "T": np.array([97.58013435402015, 81.09151713962103, 35.81980709285807]),
535 "B": np.array([2.419865645979866, 18.90848286037897, 64.18019290714193]),
536 "A": np.array([0.0, 0.0, 0.0]),
537 }
538 expected_cod = 0.9999999999993268
539 assert_fit(fit, popt_expected, contribution_expected, expected_cod)
541 # ---------------------------------------------------- NOISY ---------------------------------------------------
543 test_data = BTModelTRMC().generate_decays(0.05)
544 fit = BTModelTRMC().fit(*test_data)
545 popt_expected = {
546 "k_T": 0.010116290480286557,
547 "k_B": 4.802023013869001e-19,
548 "mu": 10.05269417362719,
549 "k_A": 0.0,
550 "y_0": 0.0,
551 "N_0": 1000000000000000.0,
552 }
553 contribution_expected = {
554 "T": np.array([97.69896938291939, 81.83087367000402, 36.82379525515391]),
555 "B": np.array([2.3010306170806087, 18.16912632999597, 63.17620474484609]),
556 "A": np.array([0.0, 0.0, 0.0]),
557 }
558 expected_cod = 0.9184744027161614
559 assert_fit(fit, popt_expected, contribution_expected, expected_cod)
561 # ------------------------------------------- NO NOISE / AUGER GUESS ------------------------------------------
563 test_data = BTModelTRMC().generate_decays()
564 model = BTModelTRMC()
565 model.fvalues["k_A"] = None
566 fit = model.fit(*test_data)
567 popt_expected = {
568 "k_T": 0.009999999999996585,
569 "k_B": 5.000000000015119e-19,
570 "k_A": 9.99999711161973e-41,
571 "mu": 9.999999999997167,
572 "y_0": 0.0,
573 "N_0": 1000000000000000.0,
574 }
575 contribution_expected = {
576 "T": np.array([97.58015916566838, 81.09165394240388, 35.819832181412764]),
577 "B": np.array([2.4198405129812923, 18.908321681647912, 64.1794233913077]),
578 "A": np.array([3.2135031959224633e-07, 2.4375948198990817e-05, 0.0007444272795414486]),
579 }
580 expected_cod = 1.0
581 assert_fit(fit, popt_expected, contribution_expected, expected_cod)
583 def test_grid_fitting(self) -> None:
585 # -------------------------------------------------- NO NOISE --------------------------------------------------
587 xs_data, ys_data, N0s = BTModelTRMC().generate_decays()
588 analysis = BTModelTRMC().grid_fitting(None, N0s, xs_data=xs_data, ys_data=ys_data)
589 popt_expected = {
590 "k_B": 5.000049117261053e-19,
591 "k_T": 0.009999990976318993,
592 "mu": 9.999999709727632,
593 "k_A": 0.0,
594 "y_0": 0.0,
595 "N_0": 1000000000000000.0,
596 }
597 contribution_expected = {
598 "T": np.array([97.58013435051565, 81.09151711799227, 35.81980706408841]),
599 "B": np.array([2.4198656494843624, 18.908482882007725, 64.18019293591158]),
600 "A": np.array([0.0, 0.0, 0.0]),
601 }
602 expected_cod = 0.9999999999993268
603 assert_fit(analysis[0], popt_expected, contribution_expected, expected_cod)
605 popt_expected = {
606 "k_B": 5.000049117289082e-19,
607 "k_T": 0.00999999097631548,
608 "mu": 9.999999709742937,
609 "k_A": 0.0,
610 "y_0": 0.0,
611 "N_0": 1000000000000000.0,
612 }
613 contribution_expected = {
614 "T": np.array([97.5801343504991, 81.09151711790633, 35.81980706397423]),
615 "B": np.array([2.419865649500915, 18.90848288209365, 64.18019293602576]),
616 "A": np.array([0.0, 0.0, 0.0]),
617 }
618 expected_cod = 0.9999999999993268
619 assert_fit(analysis[-1], popt_expected, contribution_expected, expected_cod)
621 # ---------------------------------------------------- NOISY ---------------------------------------------------
623 xs_data, ys_data, N0s = BTModelTRMC().generate_decays(0.05)
624 analysis = BTModelTRMC().grid_fitting(None, N0s, xs_data=xs_data, ys_data=ys_data)
625 popt_expected = {
626 "k_B": 4.802021953300244e-19,
627 "k_T": 0.010116289289924205,
628 "mu": 10.052693492580108,
629 "k_A": 0.0,
630 "y_0": 0.0,
631 "N_0": 1000000000000000.0,
632 }
633 contribution_expected = {
634 "T": np.array([97.6989696130472, 81.83087511653866, 36.82379726558527]),
635 "B": np.array([2.3010303869527995, 18.169124883461336, 63.17620273441473]),
636 "A": np.array([0.0, 0.0, 0.0]),
637 }
638 expected_cod = 0.9184744027161531
639 assert_fit(analysis[0], popt_expected, contribution_expected, expected_cod)
641 popt_expected = {
642 "k_B": 4.802026294974155e-19,
643 "k_T": 0.010116289206037831,
644 "mu": 10.052694327271345,
645 "k_A": 0.0,
646 "y_0": 0.0,
647 "N_0": 1000000000000000.0,
648 }
649 contribution_expected = {
650 "T": np.array([97.69896757743622, 81.83086232245338, 36.82377951378882]),
651 "B": np.array([2.301032422563783, 18.169137677546612, 63.176220486211186]),
652 "A": np.array([0.0, 0.0, 0.0]),
653 }
654 expected_cod = 0.9184744027161734
655 assert_fit(analysis[-1], popt_expected, contribution_expected, expected_cod)
657 # ------------------------------------------- NO NOISE / AUGER GUESS -------------------------------------------
659 xs_data, ys_data, N0s = BTModelTRMC().generate_decays()
660 model = BTModelTRMC()
661 model.fvalues["k_A"] = None
662 analysis = model.grid_fitting(None, N0s, xs_data=xs_data, ys_data=ys_data)
663 popt_expected = {
664 "k_B": 4.999999999957749e-19,
665 "k_T": 0.010000000000002705,
666 "k_A": 9.999973258931717e-41,
667 "mu": 9.999999999984738,
668 "y_0": 0.0,
669 "N_0": 1000000000000000.0,
670 }
671 contribution_expected = {
672 "T": np.array([97.58015916568964, 81.09165394262986, 35.819832182075714]),
673 "B": np.array([2.4198405129608025, 18.908321681480082, 64.17942339242039]),
674 "A": np.array([3.213495530872035e-07, 2.4375890055846062e-05, 0.0007444255039074176]),
675 }
676 expected_cod = 1.0
677 assert_fit(analysis[0], popt_expected, contribution_expected, expected_cod)
679 popt_expected = {
680 "k_B": 3.898375046878196e-19,
681 "k_T": 0.010363531958922218,
682 "k_A": 2.3293175945341933e-36,
683 "mu": 10.106211747446103,
684 "y_0": 0.0,
685 "N_0": 1000000000000000.0,
686 }
687 contribution_expected = {
688 "T": np.array([98.15786554403202, 84.42608368385423, 36.67047807117695]),
689 "B": np.array([1.8348477970950738, 14.993304359125895, 47.42593643224026]),
690 "A": np.array([0.007286658872892999, 0.5806119570198793, 15.903585496582778]),
691 }
692 expected_cod = 0.9995910035912059
693 assert_fit(analysis[-1], popt_expected, contribution_expected, expected_cod)
696# ------------------------------------------------------ BTD MODEL -----------------------------------------------------
699class TestBTDModel:
701 @pytest.fixture
702 def model(self) -> BTDModel:
703 """Example BTDModel"""
705 return BTDModel(["k_B", "k_T", "k_D", "N_T", "p_0", "mu_e", "mu_h"])
707 def test_initialization(self, model) -> None:
709 assert model.units["k_B"] == "cm3/ns"
710 assert model.factors["k_B"] == 1e-20
711 assert model.n_keys == ["n_e", "n_t", "n_h"]
713 def test_rate_equations(self, model) -> None:
715 result = model._rate_equations(n_e=1e17, n_t=1e12, n_h=1e17, **BTD_KWARGS)
716 assert result == {"n_e": -5711250000000000.0, "n_t": 707919948000000.0, "n_h": -5003330052000000.0}
718 def test_calculate_concentrations(self, model) -> None:
720 # Single pulse
721 output = model._calculate_concentrations(T, 1e17, **BTD_KWARGS)
722 assert np.allclose(output["n_e"][0][:3], np.array([1.00000000e17, 9.51739441e16, 9.08408676e16]))
723 assert np.allclose(output["n_t"][0][:3], np.array([0.00000000e00, 5.96016786e13, 5.96021105e13]))
724 assert np.allclose(output["n_h"][0][:3], np.array([1.00000000e17, 9.52335458e16, 9.09004697e16]))
725 assert np.allclose(output["n_e"][0][:3] + output["n_t"][0][:3], output["n_h"][0][:3])
727 # Multiple pulses
728 output = model._calculate_concentrations(T, 1e17, **BTD_KWARGS, p=1000)
729 assert np.allclose(output["n_e"][-1][:3], np.array([1.16972060e17, 1.10497012e17, 1.04700620e17]))
730 assert np.allclose(output["n_t"][-1][:3], np.array([5.95997634e13, 5.96022059e13, 5.96021815e13]))
731 assert np.allclose(output["n_h"][-1][:3], np.array([1.17031660e17, 1.10556615e17, 1.04760222e17]))
732 assert np.allclose(output["n_e"][-1][:3] + output["n_t"][-1][:3], output["n_h"][-1][:3])
733 assert len(output["n_e"]) == 5
735 # Multiple pulses - No stabilisation
736 with pytest.raises(AssertionError):
737 model._calculate_concentrations(T, 1e17, **BTD_KWARGS, p=3)
739 def test_get_carrier_concentrations(self, model) -> None:
741 popts = [{"I0": 1.0, "N_0": 1e17, **BTD_KWARGS}]
743 # Period provided
744 output = model.get_carrier_concentrations([T], popts, 100)
745 assert np.allclose(output[2][0]["n_e"][:3], np.array([1.00000000e17, 9.74992405e16, 9.51739441e16]))
746 assert np.allclose(output[2][0]["n_t"][:3], np.array([0.00000000e00, 5.94487983e13, 5.96016786e13]))
747 assert np.allclose(output[2][0]["n_h"][:3], np.array([1.00000000e17, 9.75586893e16, 9.52335458e16]))
748 assert np.allclose(output[0][0][:3], np.array([0.0, 0.00498008, 0.00996016]))
750 # Period not provided
751 output = model.get_carrier_concentrations([T], popts, 0)
752 assert np.allclose(output[2][0]["n_e"][:3], np.array([1.00000000e17, 9.51739441e16, 9.08408676e16]))
753 assert np.allclose(output[2][0]["n_t"][:3], np.array([0.00000000e00, 5.96016786e13, 5.96021105e13]))
754 assert np.allclose(output[2][0]["n_h"][:3], np.array([1.00000000e17, 9.52335458e16, 9.09004697e16]))
755 assert np.allclose(output[0][0][:3], np.array([0.0, 1.0, 2.0]))
757 def test_get_contribution_recommendations(self, model) -> None:
759 contributions = {"B": np.array([5]), "T": np.array([5]), "D": np.array([8])}
760 recs = model.get_contribution_recommendations(contributions)
761 expected = [
762 "This fit predicts low bimolecular. The values associated with this process may be inaccurate.\nIt "
763 "is recommended to measure your sample under higher excitation fluence for this process to become significant",
764 "This fit predicts low trapping. The values associated with this process may be inaccurate.\nIt is recommended to "
765 "measure your sample under lower excitation fluence for this process to become significant",
766 "This fit predicts low detrapping. The values associated with this process may be inaccurate.",
767 "Note: For the bimolecular-trapping-detrapping model, although a low contribution suggests that the "
768 "parameter associated with the process are not be accurate, a non-negligible contribution does not "
769 "automatically indicate that the parameters retrieved are accurate due to the complex nature of the "
770 "model. It is recommended to perform a grid fitting analysis with this model.",
771 ]
772 assert recs == expected
774 def test_calculate_trpl(self, model) -> None:
776 result = model.calculate_trpl(T, N_0=1e15, **BTD_KWARGS)
777 assert np.allclose(result[:3], np.array([1.0, 0.99221134, 0.98523199]))
779 def test_calculate_trmc(self, model) -> None:
781 result = model.calculate_trmc(T, N_0=1e17, **BTD_KWARGS)
782 assert np.allclose(result[:3], np.array([50.0, 47.60485258, 45.43831441]))
785class TestBTDModelTRPL:
787 def test_calculate_fit_quantity(self) -> None:
789 result = BTDModelTRPL().calculate_fit_quantity(T, N_0=1e15, **BTD_KWARGS)
790 assert np.allclose(result[:3], np.array([1.0, 0.99221134, 0.98523199]))
792 def test_calculate_contributions(self) -> None:
794 concentrations = BTDModelTRPL()._calculate_concentrations(T, 1e15, **BTD_KWARGS)
795 concentrations = {key: value[0] for key, value in concentrations.items()}
796 contributions = BTDModelTRPL().calculate_contributions(T, **concentrations, **BTD_KWARGS)
797 expected = {
798 "T": np.float64(41.01551259497901),
799 "B": np.float64(56.48980101942542),
800 "D": np.float64(2.494686385595578),
801 }
802 assert contributions == expected
804 def test_get_carrier_accumulation(self) -> None:
806 N0s = [1e17, 1e18]
807 popts = [{"N_0": n, **BTD_KWARGS} for n in N0s]
809 # 100 ns period
810 output = BTDModelTRPL().get_carrier_accumulation(popts, 100)
811 ca_expected = [np.float64(4.624820971652416), np.float64(0.5713873355827237)]
812 decay_expected = [1.0, 0.99989804, 0.9998979]
813 assert are_close(output["CA"], ca_expected)
814 assert are_close(output["Pulse S"][-1][:3], decay_expected)
816 # 50 ns period
817 output = BTDModelTRPL().get_carrier_accumulation(popts, 50)
818 ca_expected = [np.float64(7.852802160625521), np.float64(1.1155396319428357)]
819 decay_expected = [1.0, 0.99989615, 0.99989602]
820 assert are_close(output["CA"], ca_expected)
821 assert are_close(output["Pulse S"][-1][:3], decay_expected)
823 def test_generate_decays(self) -> None:
825 # Without noise
826 xs_data, ys_data, N0s = BTDModelTRPL().generate_decays()
827 assert are_close(ys_data[0][:3], [1.0, 0.93171234, 0.87145003])
828 assert are_close(ys_data[-1][:3], [1.0, 0.9437801, 0.90364939])
830 # With noise
831 xs_data, ys_data, N0s = BTDModelTRPL().generate_decays(noise=0.02)
832 assert are_close(ys_data[0][:3], [1.0, 0.96670037, 0.89900986])
833 assert are_close(ys_data[-1][:3], [1.0, 0.98174779, 0.93649283])
835 def test_fit(self) -> None:
837 # -------------------------------------------------- NO NOISE --------------------------------------------------
839 test_data = BTDModelTRPL().generate_decays()
840 fit = BTDModelTRPL().fit(*test_data)
841 popt_expected = {
842 "k_B": 5.000000052524678e-19,
843 "k_T": 1.2000000020860115e-16,
844 "k_D": 7.99999965517838e-19,
845 "N_T": 59999999908205.234,
846 "p_0": 65000005482068.65,
847 "y_0": 0.0,
848 "I": 1.0,
849 "N_0": 51000000000000.0,
850 }
851 contribution_expected = {
852 "T": np.array([97.55966, 67.41144, 28.74199, 10.17702, 5.60928]),
853 "B": np.array([1.80154, 23.92938, 61.86576, 85.42388, 92.5306]),
854 "D": np.array([0.6388, 8.65918, 9.39225, 4.3991, 1.86012]),
855 }
856 expected_cod = 1.0
857 assert_fit(fit, popt_expected, contribution_expected, expected_cod)
859 # ---------------------------------------------------- NOISY ---------------------------------------------------
861 test_data = BTDModelTRPL().generate_decays(0.05)
862 fit = BTDModelTRPL().fit(*test_data)
863 popt_expected = {
864 "k_B": 5.281696267903158e-19,
865 "k_T": 1.2005725792145259e-16,
866 "k_D": 5.907688024451682e-19,
867 "N_T": 58111233208509.96,
868 "p_0": 108786247871822.42,
869 "y_0": 0.0,
870 "I": 1.0,
871 "N_0": 51000000000000.0,
872 }
873 contribution_expected = {
874 "T": np.array([96.85071, 63.90628, 25.56837, 8.67334, 4.91477]),
875 "B": np.array([2.65371, 29.80912, 67.7983, 88.26982, 93.80589]),
876 "D": np.array([0.49558, 6.2846, 6.63334, 3.05684, 1.27934]),
877 }
878 expected_cod = 0.9219066553006975
879 assert_fit(fit, popt_expected, contribution_expected, expected_cod)
881 # --------------------------------------------- NOISY / NON-FIXED I --------------------------------------------
883 test_data = BTDModelTRPL().generate_decays(0.05)
884 model = BTDModelTRPL()
885 model.fvalues["I"] = None
886 fit = model.fit(*test_data)
887 popt_expected = {
888 "k_B": 5.231708686847702e-19,
889 "k_T": 1.187069255529963e-16,
890 "k_D": 6.120441223117572e-19,
891 "N_T": 60333138581717.695,
892 "p_0": 97515750573609.22,
893 "I": 1.0431146652007544,
894 "y_0": 0.0,
895 "N_0": 51000000000000.0,
896 }
897 contribution_expected = {
898 "T": np.array([97.19901, 65.60995, 26.60699, 9.08448, 5.11529]),
899 "B": np.array([2.31978, 27.78817, 66.25586, 87.61144, 93.49959]),
900 "D": np.array([0.48121, 6.60188, 7.13715, 3.30408, 1.38512]),
901 }
902 expected_cod = 0.9221545665271867
903 assert_fit(fit, popt_expected, contribution_expected, expected_cod)
905 def test_grid_fitting(self) -> None:
907 model = BTDModelTRPL()
908 model.gvalues_range["k_B"] = [1e-20]
909 model.gvalues_range["k_T"] = [1e-16]
910 model.gvalues_range["p_0"] = [1e14]
912 # -------------------------------------------------- NO NOISE --------------------------------------------------
914 xs_data, ys_data, N0s = model.generate_decays()
915 analysis = model.grid_fitting(None, N0s, xs_data=xs_data, ys_data=ys_data)
916 popt_expected = {
917 "k_B": 5.044092519858126e-19,
918 "k_T": 1.221507144388288e-16,
919 "k_D": 3.2627489522773756e-19,
920 "p_0": 201606458996472.16,
921 "N_T": 58686486048362.56,
922 "y_0": 0.0,
923 "I": 1.0,
924 "N_0": 51000000000000.0,
925 }
926 contribution_expected = {
927 "T": np.array([96.06959, 59.97392, 22.63214, 7.46053, 4.46979]),
928 "B": np.array([3.67138, 36.55816, 73.6027, 90.75852, 94.78018]),
929 "D": np.array([0.25903, 3.46791, 3.76516, 1.78095, 0.75003]),
930 }
931 expected_cod = 0.9999689786967758
932 assert_fit(analysis[0], popt_expected, contribution_expected, expected_cod)
933 popt_expected = {
934 "k_B": 5.0000000437758285e-19,
935 "k_T": 1.200000008803637e-16,
936 "k_D": 8.000000823783076e-19,
937 "p_0": 64999983771959.49,
938 "N_T": 59999999918573.16,
939 "y_0": 0.0,
940 "I": 1.0,
941 "N_0": 51000000000000.0,
942 }
943 contribution_expected = {
944 "T": np.array([97.55966, 67.41144, 28.74199, 10.17702, 5.60928]),
945 "B": np.array([1.80154, 23.92937, 61.86576, 85.42388, 92.5306]),
946 "D": np.array([0.6388, 8.65918, 9.39225, 4.3991, 1.86012]),
947 }
948 expected_cod = 0.9999999999999998
949 assert_fit(analysis[-1], popt_expected, contribution_expected, expected_cod)
951 # ---------------------------------------------------- NOISY ---------------------------------------------------
953 xs_data, ys_data, N0s = model.generate_decays(0.05)
954 analysis = model.grid_fitting(None, N0s, xs_data=xs_data, ys_data=ys_data)
955 popt_expected = {
956 "k_B": 5.281625280355903e-19,
957 "k_T": 1.2005402935900586e-16,
958 "k_D": 5.911623649351193e-19,
959 "p_0": 108688274641093.72,
960 "N_T": 58112578119929.21,
961 "y_0": 0.0,
962 "I": 1.0,
963 "N_0": 51000000000000.0,
964 }
965 contribution_expected = {
966 "T": np.array([96.85187, 63.91184, 25.5731, 8.67546, 4.91564]),
967 "B": np.array([2.65221, 29.79925, 67.78904, 88.26562, 93.80414]),
968 "D": np.array([0.49592, 6.28891, 6.63786, 3.05892, 1.28022]),
969 }
970 expected_cod = 0.9219066554530871
971 assert_fit(analysis[0], popt_expected, contribution_expected, expected_cod)
972 popt_expected = {
973 "k_B": 5.281014612329833e-19,
974 "k_T": 1.2004301649017149e-16,
975 "k_D": 5.952924043139263e-19,
976 "p_0": 107673071106750.62,
977 "N_T": 58122054681198.03,
978 "y_0": 0.0,
979 "I": 1.0,
980 "N_0": 51000000000000.0,
981 }
982 contribution_expected = {
983 "T": np.array([96.86386, 63.9668, 25.62092, 8.69737, 4.92498]),
984 "B": np.array([2.63673, 29.69927, 67.6943, 88.22221, 93.78573]),
985 "D": np.array([0.49942, 6.33393, 6.68478, 3.08042, 1.28929]),
986 }
987 expected_cod = 0.9219066470404309
988 assert_fit(analysis[-1], popt_expected, contribution_expected, expected_cod)
990 # -------------------------------------------- NO NOISE/ NON-FIXED I -------------------------------------------
992 xs_data, ys_data, N0s = model.generate_decays(0.05)
993 model.fvalues["I"] = None
994 model.gvalues_range["k_D"] = [1e-18]
995 analysis = model.grid_fitting(None, N0s, xs_data=xs_data, ys_data=ys_data)
996 popt_expected = {
997 "k_B": 5.233892964974858e-19,
998 "k_T": 1.1867745425973867e-16,
999 "k_D": 6.055792534709746e-19,
1000 "p_0": 98983350486299.22,
1001 "N_T": 60330106371418.1,
1002 "I": 1.0430840799173018,
1003 "y_0": 0.0,
1004 "N_0": 51000000000000.0,
1005 }
1006 contribution_expected = {
1007 "T": np.array([97.18283, 65.53522, 26.53384, 9.04918, 5.09949]),
1008 "B": np.array([2.34114, 27.93346, 66.40415, 87.68164, 93.53021]),
1009 "D": np.array([0.47603, 6.53132, 7.06201, 3.26918, 1.3703]),
1010 }
1011 expected_cod = 0.9221545717141318
1012 assert_fit(analysis[0], popt_expected, contribution_expected, expected_cod)
1013 popt_expected = {
1014 "k_B": 5.231318664511353e-19,
1015 "k_T": 1.1868317014121706e-16,
1016 "k_D": 6.1288804671095715e-19,
1017 "p_0": 97302985162596.94,
1018 "N_T": 60340004786445.39,
1019 "I": 1.0431027851890577,
1020 "y_0": 0.0,
1021 "N_0": 51000000000000.0,
1022 }
1023 contribution_expected = {
1024 "T": np.array([97.20144, 65.62315, 26.61848, 9.08953, 5.11716]),
1025 "B": np.array([2.31668, 27.76541, 66.23378, 87.60143, 93.49561]),
1026 "D": np.array([0.48187, 6.61144, 7.14774, 3.30904, 1.38723]),
1027 }
1028 expected_cod = 0.9221545624359501
1029 assert_fit(analysis[-1], popt_expected, contribution_expected, expected_cod)
1032class TestBTDModelTRMC:
1034 def test_calculate_fit_quantity(self) -> None:
1036 result = BTDModelTRMC().calculate_fit_quantity(T, N_0=1e17, **BTD_KWARGS)
1037 assert np.allclose(result[:3], np.array([50.0, 47.60485258, 45.43831441]))
1039 def test_calculate_contributions(self) -> None:
1041 concentrations = BTDModelTRMC()._calculate_concentrations(T, 1e15, **BTD_KWARGS)
1042 concentrations = {key: value[0] for key, value in concentrations.items()}
1043 contributions = BTDModelTRMC().calculate_contributions(T, **concentrations, **BTD_KWARGS)
1044 expected = {
1045 "T": np.float64(33.7114972857804),
1046 "B": np.float64(62.7295706296424),
1047 "D": np.float64(3.558932084577181),
1048 }
1049 assert contributions == expected
1051 def test_get_carrier_accumulation(self) -> None:
1053 N0s = [1e17, 1e18]
1054 popts = [{"N_0": n, **BTD_KWARGS} for n in N0s]
1056 # 100 ns period
1057 output = BTDModelTRMC().get_carrier_accumulation(popts, 100)
1058 ca_expected = [np.float64(3.912967640970455), np.float64(0.48314189311882694)]
1059 decay_expected = [50.97705122, 50.97445232, 50.97444872]
1060 assert are_close(output["CA"], ca_expected)
1061 assert are_close(output["Pulse S"][-1][:3], decay_expected)
1063 # 50 ns period
1064 output = BTDModelTRMC().get_carrier_accumulation(popts, 50)
1065 ca_expected = [np.float64(6.63910507746735), np.float64(0.9422743536986633)]
1066 decay_expected = [51.92211319, 51.91941704, 51.9194135]
1067 assert are_close(output["CA"], ca_expected)
1068 assert are_close(output["Pulse S"][-1][:3], decay_expected)
1070 def test_generate_decays(self) -> None:
1072 # Without noise
1073 xs_data, ys_data, N0s = BTDModelTRMC().generate_decays()
1074 assert are_close(ys_data[0][:3], [50.0, 44.46133618, 41.08877032])
1075 assert are_close(ys_data[-1][:3], [50.0, 44.68626895, 40.55753199])
1077 # With noise
1078 xs_data, ys_data, N0s = BTDModelTRMC().generate_decays(noise=0.02)
1079 assert are_close(ys_data[0][:3], [48.92553402, 45.17205085, 41.50080638])
1080 assert are_close(ys_data[-1][:3], [49.25755246, 45.85575692, 41.50440705])
1082 def test_fit(self) -> None:
1084 # -------------------------------------------------- NO NOISE --------------------------------------------------
1086 test_data = BTDModelTRMC().generate_decays()
1087 fit = BTDModelTRMC().fit(*test_data)
1088 popt_expected = {
1089 "k_B": 5.000000164708902e-19,
1090 "k_T": 1.200000031835309e-16,
1091 "k_D": 7.999999808965121e-19,
1092 "N_T": 59999999376176.54,
1093 "p_0": 65000001818720.9,
1094 "mu_e": 20.000000281407857,
1095 "mu_h": 29.99999994350886,
1096 "y_0": 0.0,
1097 "N_0": 51000000000000.0,
1098 }
1099 contribution_expected = {
1100 "T": np.array([40.90276, 32.92583, 20.94199, 13.13185, 9.81368]),
1101 "B": np.array([1.59628, 21.39947, 52.96585, 75.52489, 84.90275]),
1102 "D": np.array([57.50096, 45.6747, 26.09216, 11.34326, 5.28357]),
1103 }
1104 expected_cod = 1.0
1105 assert_fit(fit, popt_expected, contribution_expected, expected_cod)
1107 # ---------------------------------------------------- NOISY ---------------------------------------------------
1109 test_data = BTDModelTRMC().generate_decays(0.05)
1110 fit = BTDModelTRMC().fit(*test_data)
1111 popt_expected = {
1112 "k_B": 5.170253809634543e-19,
1113 "k_T": 1.1017602528332628e-16,
1114 "k_D": 8.109404996553888e-19,
1115 "N_T": 57522615670991.11,
1116 "p_0": 63326777368258.42,
1117 "mu_e": 20.819525615904908,
1118 "mu_h": 29.662688836182774,
1119 "y_0": 0.0,
1120 "N_0": 51000000000000.0,
1121 }
1122 contribution_expected = {
1123 "T": np.array([41.93316, 33.03053, 20.50887, 12.50261, 9.1934]),
1124 "B": np.array([1.91827, 23.1565, 54.71674, 76.79712, 85.84359]),
1125 "D": np.array([56.14857, 43.81296, 24.7744, 10.70027, 4.96301]),
1126 }
1127 expected_cod = 0.9072435319931186
1128 assert_fit(fit, popt_expected, contribution_expected, expected_cod)
1130 def test_grid_fitting(self) -> None:
1132 model = BTDModelTRMC()
1133 model.gvalues_range["k_B"] = [1e-20]
1134 model.gvalues_range["k_T"] = [1e-16]
1135 model.gvalues_range["k_D"] = [1e-18]
1136 model.gvalues_range["p_0"] = [1e14]
1138 # -------------------------------------------------- NO NOISE --------------------------------------------------
1140 xs_data, ys_data, N0s = model.generate_decays()
1141 analysis = model.grid_fitting(None, N0s, xs_data=xs_data, ys_data=ys_data)
1142 popt_expected = {
1143 "k_B": 5.000000263245968e-19,
1144 "k_T": 1.200000041725577e-16,
1145 "k_D": 7.999999617651796e-19,
1146 "p_0": 65000003889440.46,
1147 "N_T": 59999999181832.375,
1148 "mu_e": 20.000000542628477,
1149 "mu_h": 29.9999999067198,
1150 "y_0": 0.0,
1151 "N_0": 51000000000000.0,
1152 }
1153 contribution_expected = {
1154 "T": np.array([40.90276, 32.92583, 20.94199, 13.13185, 9.81368]),
1155 "B": np.array([1.59628, 21.39947, 52.96585, 75.52489, 84.90275]),
1156 "D": np.array([57.50096, 45.6747, 26.09216, 11.34326, 5.28357]),
1157 }
1158 expected_cod = 0.9999999999999999
1159 assert_fit(analysis[0], popt_expected, contribution_expected, expected_cod)
1161 popt_expected = {
1162 "k_B": 5.000000344817324e-19,
1163 "k_T": 1.2000000694970784e-16,
1164 "k_D": 7.999999442799611e-19,
1165 "p_0": 65000005667179.016,
1166 "N_T": 59999998470043.97,
1167 "mu_e": 20.00000063847375,
1168 "mu_h": 29.999999859906556,
1169 "y_0": 0.0,
1170 "N_0": 51000000000000.0,
1171 }
1172 contribution_expected = {
1173 "T": np.array([40.90276, 32.92583, 20.94199, 13.13185, 9.81368]),
1174 "B": np.array([1.59628, 21.39947, 52.96585, 75.52489, 84.90275]),
1175 "D": np.array([57.50096, 45.6747, 26.09216, 11.34326, 5.28357]),
1176 }
1177 expected_cod = 0.9999999999999994
1178 assert_fit(analysis[-1], popt_expected, contribution_expected, expected_cod)
1180 # ---------------------------------------------------- NOISY ---------------------------------------------------
1182 xs_data, ys_data, N0s = model.generate_decays(0.05)
1183 analysis = model.grid_fitting(None, N0s, xs_data=xs_data, ys_data=ys_data)
1184 popt_expected = {
1185 "k_B": 5.169968590421277e-19,
1186 "k_T": 1.1017306106751546e-16,
1187 "k_D": 8.120500700336253e-19,
1188 "p_0": 63209041226835.805,
1189 "N_T": 57509469769832.9,
1190 "mu_e": 20.816413593836856,
1191 "mu_h": 29.663528347986045,
1192 "y_0": 0.0,
1193 "N_0": 51000000000000.0,
1194 }
1195 contribution_expected = {
1196 "T": np.array([41.92978, 33.03175, 20.51353, 12.50502, 9.19378]),
1197 "B": np.array([1.91722, 23.14519, 54.69963, 76.78697, 85.83906]),
1198 "D": np.array([56.153, 43.82306, 24.78684, 10.70801, 4.96716]),
1199 }
1200 expected_cod = 0.9072435361184141
1201 assert_fit(analysis[0], popt_expected, contribution_expected, expected_cod)
1202 popt_expected = {
1203 "k_B": 5.170343293253923e-19,
1204 "k_T": 1.1018194467749615e-16,
1205 "k_D": 8.121236402177761e-19,
1206 "p_0": 63200380749480.586,
1207 "N_T": 57501357284825.3,
1208 "mu_e": 20.817089249169957,
1209 "mu_h": 29.662940880799475,
1210 "y_0": 0.0,
1211 "N_0": 51000000000000.0,
1212 }
1213 contribution_expected = {
1214 "T": np.array([41.93091, 33.03105, 20.51274, 12.50452, 9.19342]),
1215 "B": np.array([1.91766, 23.14931, 54.70297, 76.78871, 85.84002]),
1216 "D": np.array([56.15143, 43.81964, 24.78429, 10.70677, 4.96656]),
1217 }
1218 expected_cod = 0.9072435358895351
1219 assert_fit(analysis[-1], popt_expected, contribution_expected, expected_cod)