Validate Somersalo scaling law

Here we show how we can compute multipactor thresholds measured for different reflection coefficient, and compare this evolution to Somersalo scaling law. We also illustrate some ThresholdSet manipulations.

Load data

[1]:
from functools import partial

from multipac_testbench import AveragedThresholdSet, TestCampaign, ThresholdSet
from multipac_testbench.data import config_path
from multipac_testbench.data.multipactor_tests import tests_140
from multipac_testbench.instruments import CurrentProbe, ForwardPower
from multipac_testbench.util.multipactor_detectors import quantity_is_above_threshold

freqs = (140.0, 140.0, 140.0, 140.0)
swrs = (4.0, 3.0, 2.0, 1.0)

test_campaign =  TestCampaign.from_filepaths(
    tests_140,
    freqs,
    swrs,
    config_path,
    is_raw=True
)
[INFO    ] [loader.py           ] Applied trigger_policy = 'keep_all' on /home/placais/Documents/simulation/python/multipac_testbench/src/multipac_testbench/data/multipactor_tests/2025.06.20_140MHz-SWR4-11.csv
[ERROR   ] [factory.py          ] column_header = 'NI9205_E1' not present in provided file. Skipping associated instrument.
[INFO    ] [loader.py           ] Applied trigger_policy = 'keep_all' on /home/placais/Documents/simulation/python/multipac_testbench/src/multipac_testbench/data/multipactor_tests/2025.06.20_140MHz-SWR3-12.csv
[ERROR   ] [factory.py          ] column_header = 'NI9205_E1' not present in provided file. Skipping associated instrument.
[INFO    ] [loader.py           ] Applied trigger_policy = 'keep_all' on /home/placais/Documents/simulation/python/multipac_testbench/src/multipac_testbench/data/multipactor_tests/2025.06.20_140MHz-SWR2-13.csv
[ERROR   ] [factory.py          ] column_header = 'NI9205_E1' not present in provided file. Skipping associated instrument.
[INFO    ] [loader.py           ] Applied trigger_policy = 'keep_all' on /home/placais/Documents/simulation/python/multipac_testbench/src/multipac_testbench/data/multipactor_tests/2025.06.20_140MHz-SWR1-14.csv
[ERROR   ] [factory.py          ] column_header = 'NI9205_E1' not present in provided file. Skipping associated instrument.
/home/placais/Documents/simulation/python/multipac_testbench/src/multipac_testbench/util/physics.py:28: RuntimeWarning: invalid value encountered in sqrt
  reflection_coefficient = np.abs(np.sqrt(reflected_power / forward_power))
/home/placais/Documents/simulation/python/multipac_testbench/src/multipac_testbench/util/physics.py:28: RuntimeWarning: invalid value encountered in sqrt
  reflection_coefficient = np.abs(np.sqrt(reflected_power / forward_power))

Calculate multipactor thresholds

[2]:
current_multipactor_criterions = {'threshold': 14., 'minimum_number_of_points': 1}
current_multipac_detector = partial(quantity_is_above_threshold, **current_multipactor_criterions)

As Somersalo scaling law concerns power thresholds, we look for “global” multipactor criterions:

[3]:
merged = test_campaign.determine_thresholds(
    current_multipac_detector,
    CurrentProbe,
    threshold_predicate=lambda t: t.sample_index > 300,  # Only consider Thresholds measured after 300th power step
    threshold_reducer="any",                   # Keyword to merge the multipactor zones
)
[WARNING ] [threshold.py        ] User should manually trim exceedent powers in order to avoid flat minima at the end of the test.
[WARNING ] [threshold.py        ] User should manually trim exceedent powers in order to avoid flat minima at the end of the test.
[WARNING ] [threshold.py        ] User should manually trim exceedent powers in order to avoid flat minima at the end of the test.
[WARNING ] [threshold.py        ] User should manually trim exceedent powers in order to avoid flat minima at the end of the test.

Check Somersalo scaling law

Naive thresholds

From there, you have several options. You can simply use these thresholds:
[4]:
_ = test_campaign.check_somersalo_scaling_law(merged, figsize=(8, 8))
../../_images/manual_notebooks_check_somersalo_scaling_law_11_0.png

Extreme thresholds

You can filter out the thresholds, to keep only one lower and one upper threshold per half-power cycle.

[5]:
extreme = {test: ThresholdSet.extreme(threshold_set) for test, threshold_set in merged.items()}
_ = test_campaign.check_somersalo_scaling_law(extreme, figsize=(8, 8))
../../_images/manual_notebooks_check_somersalo_scaling_law_14_0.png

Average thresholds

We average the “global” multipactor thresholds measured during the last power step:

Note

AveragedThresholdSet is an object derived from ThresholdSet. Internally, when you call AveragedThresholdSet.data_at_thresholds(<AnInstrumentType>), it will gather the signal measured by <AnInstrumentType> at all lower and upper thresholds, and return the median of those signals.

[6]:
averaged = {test: AveragedThresholdSet.from_threshold_set(threshold_set) for test, threshold_set in merged.items()}
_ = test_campaign.check_somersalo_scaling_law(averaged, figsize=(8, 8))
../../_images/manual_notebooks_check_somersalo_scaling_law_17_0.png