def __init__( self, ctx ): "multiple flows are scheduled at the same time" capacity = float(ctx.config.get("topo.capacity", 100)) propagation_delay = float(ctx.config.get("topo.propagation_delay", 0.5)) processing_delay = float(ctx.config.get("topo.processing_delay", 0)) # initialize topology Topo.__init__( self ) s1 = self.addSwitch( 's1', x=3, y=2, processing_delay=processing_delay ) host1 = self.addHost( 'h1', x=4, y=1) host2 = self.addHost( 'h2',x=4, y=3) self.addLink( host1, s1, capacity=capacity, propagation_delay=propagation_delay ) self.addLink( host2, s1, capacity=capacity, propagation_delay=propagation_delay ) # add traffic self.addTraffic( dict(fg_class='Single', fg_label="f0", fg_start=0, fg_demand=100, fg_duration=10, fg_fixed_path=['h1', 's1', 'h2']), dict(fg_class='Single', fg_label="f1", fg_start=0, fg_demand=100, fg_duration=10, fg_fixed_path=['h1', 's1', 'h2']), dict(fg_class='Single', fg_label="f2", fg_start=0, fg_demand=100, fg_duration=10, fg_fixed_path=['h1', 's1', 'h2']), ) # call on_done if simulation is finished ctx.on_test_finished = self.on_done
def __init__( self, ctx ): propagation_delay = float(ctx.config.get("topo.propagation_delay", 0.25)) processing_delay = float(ctx.config.get("topo.processing_delay", 0)) # Initialize Topo.__init__( self ) ds = self.addSwitch( 'DS', x=2, y=1, engine=TestEngine(ctx, processing_delay=processing_delay)) es = self.addSwitch( 'ES', x=1, y=1, engine=TestEngine(ctx, processing_delay=processing_delay)) h1 = self.addHost( 'h1', x=4, y=1) h2 = self.addHost( 'h2',x=4, y=3) self.addLink( ds, es, capacity=1000, propagation_delay=propagation_delay ) self.addLink( h1, ds, capacity=1000, propagation_delay=propagation_delay ) self.addLink( h2, ds, capacity=1000, propagation_delay=propagation_delay ) # add traffic i=0 self.addTraffic( dict(fg_class='Single', fg_label="f%d" % i, fg_start=i, fg_demand=100, fg_duration=10, fg_fixed_path=['h1', 'DS', 'h2'])) # call on_done if simulation is finished ctx.on_test_finished = self.on_done
def __init__( self, ctx ): "very simple overutilization scenario without any delays" capacity = float(ctx.config.get("topo.capacity", 10)) propagation_delay = float(ctx.config.get("topo.propagation_delay", 0)) processing_delay = float(ctx.config.get("topo.processing_delay", 0)) # initialize topology Topo.__init__( self ) s1 = self.addSwitch( 's1', x=3, y=2, processing_delay=processing_delay ) host1 = self.addHost( 'h1', x=4, y=1) host2 = self.addHost( 'h2',x=4, y=3) self.addLink( host1, s1, capacity=capacity, propagation_delay=propagation_delay ) self.addLink( host2, s1, capacity=capacity, propagation_delay=propagation_delay ) # add traffic self.addTraffic( dict(fg_class='Single', fg_label="f0", fg_start=0, fg_demand=100, fg_duration=10, fg_fixed_path=['h1', 's1', 'h2']), dict(fg_class='Single', fg_label="f1", fg_start=5, fg_demand=100, fg_duration=10, fg_fixed_path=['h1', 's1', 'h2']), dict(fg_class='Single', fg_label="f2", fg_start=8, fg_demand=20, fg_duration=2, fg_fixed_path=['h1', 's1', 'h2']), ) # call on_done if simulation is finished ctx.on_test_finished = self.on_done
def __init__(self, ctx): """ Two flows compete for the bottleneck; green flow has a datarate of 5/sec red flow has a datarate of 20/sec --> fairshare datarate is 8/sec for red and 1/sec for green; (h1->s1) Flow F1 data: red: 5.5 to 18 = 12.5 * 8 = 100 green: 0.5 to 5.5 with 5 = 25 5.5 to 18 with 1 = 12.5 18 to 30.5 with 5 = 12.5*5 = 62.5 sum is 100 spare capacity? """ propagation_delay = float(ctx.config.get("topo.propagation_delay", 0.5)) processing_delay = float(ctx.config.get("topo.processing_delay", 0)) # Initialize Topo.__init__(self) s1 = self.addSwitch('s1', x=3, y=2, processing_delay=processing_delay) s2 = self.addSwitch('s2', x=3, y=2, processing_delay=processing_delay) s3 = self.addSwitch('s3', x=3, y=2, processing_delay=processing_delay) host1 = self.addHost('h1', x=4, y=1) host2 = self.addHost('h2', x=4, y=3) self.addLink(host1, s1, capacity=20, propagation_delay=propagation_delay) self.addLink(s1, s2, capacity=20, propagation_delay=propagation_delay) self.addLink(s2, s3, capacity=10, propagation_delay=propagation_delay) self.addLink(host2, s3, capacity=20, propagation_delay=propagation_delay) # add traffic self.addTraffic( dict(fg_class='Single', fg_label="f0", fg_start=0, fg_demand=100, fg_duration=20, fg_fixed_path=['h1', 's1', 's2', 's3', 'h2'], fg_color='g'), dict(fg_class='Single', fg_label="f1", fg_start=5, fg_demand=100, fg_duration=5, fg_fixed_path=['h1', 's1', 's2', 's3', 'h2'], fg_color='r'), ) # call on_done if simulation is finished ctx.on_test_finished = self.on_done
def __init__(self, ctx): "scenario with static path that has a 'loop' between ds and es" propagation_delay = float(ctx.config.get("topo.propagation_delay", 0.5)) processing_delay = float(ctx.config.get("topo.processing_delay", 0)) # initialize topology Topo.__init__(self) ds = self.addSwitch('ds', x=3, y=2, processing_delay=processing_delay) es = self.addSwitch('es', x=2, y=2, processing_delay=processing_delay) es2 = self.addSwitch('es2', x=1, y=2, processing_delay=processing_delay) h1 = self.addHost('h1', x=4, y=1) h2 = self.addHost('h2', x=4, y=3) self.addLink(h1, ds, capacity=1000, propagation_delay=propagation_delay) self.addLink(h2, ds, capacity=1000, propagation_delay=propagation_delay) self.addLink(ds, es, capacity=1000, propagation_delay=propagation_delay) self.addLink(es, es2, capacity=1000, propagation_delay=propagation_delay) # add traffic self.addTraffic( dict(fg_class='Single', fg_label="Delegated", fg_start=6, fg_demand=50, fg_duration=5, fg_fixed_path=['h1', 'ds', 'es', 'ds', 'h2']), dict(fg_class='Single', fg_label="Normal", fg_start=0, fg_demand=50, fg_duration=5, fg_fixed_path=['h1', 'ds', 'h2']), dict(fg_class='Single', fg_label="Multi-Hop Delegated", fg_start=15, fg_demand=50, fg_duration=5, fg_fixed_path=['h1', 'ds', 'es', 'es2', 'es', 'ds', 'h2']), ) # call on_done if simulation is finished ctx.on_test_finished = self.on_done
def __init__(self, ctx): # Initialize Topo.__init__(self) self.ctx = ctx cnt_hosts = self.paramInt('param_topo_num_hosts', 20) cnt_cores = self.paramInt('param_topo_num_cores', 3) cnt_edges = self.paramInt('param_topo_num_edges', 3) cnt_flows = self.paramInt('param_topo_num_flows', 5000) fullmesh_core = self.paramInt('param_topo_fullmesh_core', 1) cores = [] hostnames = [] for c in range(cnt_cores): core = self.addSwitch('core%d' % c, x=1, y=1) cores.append(core) for e in range(cnt_edges): edge = self.addSwitch('edge%d.%d' % (c, e), x=1, y=2) self.addLink(core, edge) for i in range(cnt_hosts): name = 'h%d.%d.%d' % (c, e, i) host = self.addHost(name, x=1, y=3) hostnames.append(name) self.addLink(host, edge) # core switches are connected in a full mesh links = [] if fullmesh_core == 1: for i in cores: for j in cores: if not (i, j) in links and not (j, i) in links: links.append((i, j)) self.addLink(i, j) else: # core switches are only connected to their direct neighbors for i, j in zip(cores, cores[1:]): if not (i, j) in links and not (j, i) in links: links.append((i, j)) print('add', i, j) self.addLink(i, j) self.addLink(cores[-1], cores[0]) # add traffic for this host self.addTraffic( dict( fg_class='Random', #fg_seed=100, # use fixed seed (not implemented atm) fg_numflows=cnt_flows, # number of flows to generate in total fg_time_range=300, # spread epochs over 300 seconds fg_shuffle_epoch=True, # randomize shuffle array fg_epoch=[ 1, 1, 1, 1.2, 1.3, 1.5, 1.9, 2.8, 1.7, 1.1, 1, 1, 0.7, 0.5, 0.3 ], fg_source_set=[], fg_random_destination=hostnames)) # call on_done if simulation is finished ctx.on_simulation_finished = self.on_done
def __init__(self, ctx): propagation_delay = float(ctx.config.get("topo.propagation_delay", 0.5)) processing_delay = float(ctx.config.get("topo.processing_delay", 0)) # Initialize Topo.__init__(self) s1 = self.addSwitch('s1', x=3, y=2, processing_delay=processing_delay) s2 = self.addSwitch('s2', x=3, y=2, processing_delay=processing_delay) s3 = self.addSwitch('s3', x=3, y=2, processing_delay=processing_delay) h1 = self.addHost('h1', x=4, y=1) h2 = self.addHost('h2', x=4, y=3) h3 = self.addHost('h3', x=4, y=3) self.addLink(h1, s1, capacity=1000, propagation_delay=propagation_delay) self.addLink(h2, s2, capacity=1000, propagation_delay=propagation_delay) self.addLink(h3, s3, capacity=1000, propagation_delay=propagation_delay) self.addLink(s1, s2, capacity=10, propagation_delay=propagation_delay) self.addLink(s2, s3, capacity=10, propagation_delay=propagation_delay) # add traffic self.addTraffic( dict(fg_class='Single', fg_label="f0", fg_start=0, fg_demand=100, fg_duration=10, fg_fixed_path=['h1', 's1', 's2', 'h2'], fg_color='g'), dict(fg_class='Single', fg_label="f1", fg_start=5, fg_demand=100, fg_duration=10, fg_fixed_path=['h1', 's1', 's2', 's3', 'h3'], fg_color='r'), dict(fg_class='Single', fg_label="f2", fg_start=10, fg_demand=100, fg_duration=10, fg_fixed_path=['h1', 's1', 's2', 's3', 'h3'], fg_color='purple'), ) # call on_done if simulation is finished ctx.on_test_finished = self.on_done
def __init__( self, ctx, switch, **kwargs ): Topo.__init__( self ) self.ctx = ctx self.solver = None logger.info("running global_single_v3 for switch=%d" % switch) scenario = self.ctx.scenario if not scenario: raise Exception("ctx.scenario is not set, cannot continue") all_labels = sorted(self.ctx.scenario_data.ports) all_flow_rules = sorted(self.ctx.scenario_data.flows_by_id.values(), key=lambda x: x.get('start')) print("") print("required dummy switches") for label in all_labels: if label.startswith('dummy'): print(" ", label) print("") print("all ports of the simulated switch=%d (%d in total)" % (switch, len(all_labels))) for label in all_labels: print(" ", label) ds = self.addSwitch( 'DS', x=2, y=1, engine=PBCEEngineSolverV2(ctx)) es = self.addSwitch( 'ES', x=0, y=0, engine=PBCEEngineSolverV2(ctx)) self.addLink( ds, es, propagation_delay=0 ) for label in all_labels: host = self.addHost(label, x=1, y=1) self.addLink( host, ds, propagation_delay=0 ) # take traffic from constructor self.addTraffic(dict(fg_class='gen_copy', fg_params=dict( flow_params=all_flow_rules, all_labels=all_labels))) # call on_done if simulation is finished ctx.on_simulation_finished = self.on_done ctx.on_simulation_setup_complete = self.on_simulation_setup_complete
def __init__(self, ctx): propagation_delay = float(ctx.config.get("topo.propagation_delay", 0.5)) processing_delay = float(ctx.config.get("topo.processing_delay", 0)) # Initialize Topo.__init__(self) ds = self.addSwitch('DS', x=2, y=1, engine=TestEngine( ctx, processing_delay=processing_delay)) es = self.addSwitch('ES', x=1, y=1, engine=TestEngine( ctx, processing_delay=processing_delay)) s1 = self.addSwitch('s1', x=1, y=1, engine=TestEngine( ctx, processing_delay=processing_delay)) s2 = self.addSwitch('s2', x=1, y=1, engine=TestEngine( ctx, processing_delay=processing_delay)) s3 = self.addSwitch('s3', x=1, y=1, engine=TestEngine( ctx, processing_delay=processing_delay)) s4 = self.addSwitch('s4', x=1, y=1, engine=TestEngine( ctx, processing_delay=processing_delay)) h1 = self.addHost('h1', x=4, y=1) h2 = self.addHost('h2', x=4, y=3) h3 = self.addHost('h3', x=4, y=1) self.addLink(ds, es, capacity=100, propagation_delay=propagation_delay) self.addLink(ds, s1, capacity=100, propagation_delay=propagation_delay) self.addLink(ds, s2, capacity=100, propagation_delay=propagation_delay) self.addLink(ds, s3, capacity=100, propagation_delay=propagation_delay) self.addLink(s3, s2, capacity=100, propagation_delay=propagation_delay) self.addLink(s3, s4, capacity=10, propagation_delay=propagation_delay) self.addLink(s4, s2, capacity=10, propagation_delay=propagation_delay) self.addLink(h1, s1, capacity=100, propagation_delay=propagation_delay) self.addLink(h2, s4, capacity=100, propagation_delay=propagation_delay) self.addLink(h3, s1, capacity=100, propagation_delay=propagation_delay) # add traffic self.addTraffic( # green flow dict(fg_class='Single', fg_label="f0", fg_color="g", fg_start=0, fg_demand=100, fg_duration=10, fg_fixed_path=['h1', 's1', 'DS', 's3', 's4', 'h2']), # red flow dict(fg_class='Single', fg_label="f1", fg_color="r", fg_start=0, fg_demand=100, fg_duration=10, fg_fixed_path=['h1', 's1', 'DS', 's3', 's4', 'h2']), ) # # call on_done if simulation is finished ctx.on_test_finished = self.on_done
def __init__(self, ctx): Topo.__init__(self) self.ctx = ctx # IMPORTANT: all parameters that may be set to 0 HAVE to # have the default value 0! Parameters where 0 is not a valid # value can have any default value # scenario parameters self.paramInt('param_topo_scenario_generator', 1) # 1 is BA model self.paramInt( 'param_topo_seed', -1 ) # -1 --> raise exception if seed is not defined (all experiments should be reproducible) self.paramInt('param_topo_switch_capacity', 90) # 20-99 self.paramInt('param_topo_switch_capacity_overwrite', 0) # DEFAULT HAS TO BE 0!! self.paramInt('param_topo_traffic_scale_overwrite', 0) # DEFAULT HAS TO BE 0!! self.paramInt('param_topo_num_hosts', 20) self.paramInt('param_topo_num_flows', 25000) self.paramInt('param_topo_num_switches', 3) self.paramInt('param_topo_bottleneck_cnt', 0) self.paramInt('param_topo_bottleneck_duration', 0) self.paramInt('param_topo_bottleneck_intensity', 110) # 110=iat is reduced by 10% during bottleneck self.paramInt('param_topo_concentrate_demand', 0) self.paramInt('param_topo_concentrate_demand_retries', 0) self.paramInt('param_topo_scenario_ba_modelparam', 1) self.paramInt( 'param_topo_traffic_scale', 100) # 100=no scaling; > 100: more traffic; <100: less traffic self.paramInt('param_topo_traffic_interswitch', 0) # DEFAULT HAS TO BE 0!! self.paramInt('param_topo_iat_scale', 250) self.paramInt('param_topo_idle_timeout', 1) # DEFAULT HAS TO BE 1 # flow delegation parameters self.paramInt('param_dts_algo', 2) self.paramInt('param_dts_look_ahead', 3) self.paramInt('param_dts_skip_ahead', 1) self.paramInt('param_dts_timelimit', 200) self.paramInt('param_dts_objective', 3) self.paramInt('param_dts_weight_table', 0) # DEFAULT HAS TO BE 0!! self.paramInt('param_dts_weight_link', 0) # DEFAULT HAS TO BE 0!! self.paramInt('param_dts_weight_ctrl', 0) # DEFAULT HAS TO BE 0!! self.paramInt('param_rsa_algo', 2) self.paramInt('param_rsa_look_ahead', 3) self.paramInt('param_rsa_max_assignments', 50) self.paramInt('param_rsa_timelimit', 200) self.paramInt('param_rsa_weight_table', 0) # DEFAULT HAS TO BE 0!! self.paramInt('param_rsa_weight_link', 0) # DEFAULT HAS TO BE 0!! self.paramInt('param_rsa_weight_ctrl', 0) # DEFAULT HAS TO BE 0!! self.paramInt('param_rsa_weight_backup', 10000) self.paramInt('param_rsa_skip', 0) # DEFAULT HAS TO BE 0 # other parameters for verification, debugging etc self.paramInt('param_debug_total_time_limit', 600) self.paramInt('param_debug_small_statistics', 0) # DEFAULT HAS TO BE 0 self.paramInt('param_debug_verify_with_simulator', 0) # DEFAULT HAS TO BE 0 self.paramInt('param_debug_verify_analytically', 0) # DEFAULT HAS TO BE 0 self.paramInt('param_debug_export_rsa_ratings', 0) # DEFAULT HAS TO BE 0 self.paramInt('param_debug_only_run_scenario_generator', 0) # DEFAULT HAS TO BE 0 self.paramInt('param_debug_plot_demands', 0) # DEFAULT HAS TO BE 0!! self.paramInt('param_debug_plot_distributions', 0) # DEFAULT HAS TO BE 0!! self.paramInt('param_debug_plot_scenario', 0) # DEFAULT HAS TO BE 0!! self.paramInt('param_debug_plot_result', 0) # DEFAULT HAS TO BE 0!! self.paramInt('param_debug_show_result', 0) # DEFAULT HAS TO BE 0!! self.paramInt('param_debug_show_result_demands', 0) # DEFAULT HAS TO BE 0!! # param_topo_scenario_generator = 2 will randomize all parameters using the given parameter as an # upper or lower bound (the other bound parameter is determined automatically) cfg = self.ctx.config if cfg.get('param_topo_scenario_generator') == 2: # we want the same "random" scenario of the seed is identical (for reproducibility) random.seed(self.ctx.config.get('param_topo_seed')) # these are hold static self.ctx.config['param_topo_scenario_generator'] = 1 self.ctx.config['param_dts_objective'] = 5 # set default objective for DTS if not provided if self.ctx.config[ 'param_dts_weight_table'] == 0 and self.ctx.config[ 'param_dts_weight_link'] == 0 and self.ctx.config[ 'param_dts_weight_ctrl'] == 0: self.ctx.config['param_dts_weight_table'] = 0 self.ctx.config['param_dts_weight_link'] = 1 self.ctx.config['param_dts_weight_ctrl'] = 0 # set default objective for RSA if not provided if self.ctx.config[ 'param_rsa_weight_table'] == 0 and self.ctx.config[ 'param_rsa_weight_link'] == 0 and self.ctx.config[ 'param_rsa_weight_ctrl'] == 0: self.ctx.config['param_rsa_weight_table'] = 1 self.ctx.config['param_rsa_weight_link'] = 0 self.ctx.config['param_rsa_weight_ctrl'] = 5 # all with length 2 bounds = dict( param_topo_switch_capacity=[20, 99], # capacity reduction factor param_topo_num_switches=[2, 15], param_topo_num_flows=[25000, 250000], param_topo_seed=[1, 1000000], param_topo_traffic_interswitch=[20, 30, 40, 50, 60, 70, 80], param_topo_bottleneck_cnt=[ 20, 15, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 20 ], param_topo_bottleneck_duration=[1, 50], param_topo_bottleneck_intensity=[ 250, 200, 180, 160, 140, 120, 110, 125, 155, 170, 190, 210, 280 ], param_topo_concentrate_demand=[ 4, 3, 2, 2, 1, 1, 0, 0, 1, 1, 2, 2, 3, 4 ], param_topo_concentrate_demand_retries=[ 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 ], param_topo_scenario_ba_modelparam=[ 5, 4, 3, 2, 2, 1, 1, 1, 2, 2, 3, 4, 5 ], param_topo_iat_scale=[280, 300, 350, 300, 280], param_topo_idle_timeout=[1, 5]) for k, v in self.ctx.config.items(): for k2, boundaries in bounds.items(): if k == k2: if len(boundaries) == 2: randval = random.randint(boundaries[0], boundaries[1]) self.ctx.config[k] = randval if len(boundaries) > 2: randval = normal_choice(boundaries) self.ctx.config[k] = randval switch_cnt = self.ctx.config.get('param_topo_num_switches') # only randomize param_topo_switch_capacity if the parameter is not specified if self.ctx.config.get('param_topo_switch_capacity_overwrite') > 0: self.ctx.config['param_topo_switch_capacity'] = ctx.config.get( 'param_topo_switch_capacity_overwrite') if self.ctx.config.get('param_topo_bottleneck_cnt') == 0: self.ctx.config['param_topo_bottleneck_duration'] = 0 self.ctx.config['param_topo_bottleneck_intensity'] = 0 if self.ctx.config.get('param_topo_concentrate_demand') == 0: self.ctx.config['param_topo_concentrate_demand_retries'] = 0 if self.ctx.config.get( 'param_topo_scenario_ba_modelparam') >= switch_cnt: self.ctx.config[ 'param_topo_scenario_ba_modelparam'] = random.randint( 1, switch_cnt - 1) if self.ctx.config.get( 'param_topo_concentrate_demand') >= switch_cnt: self.ctx.config[ 'param_topo_concentrate_demand'] = random.randint( 0, switch_cnt - 1) self.ctx.config['param_topo_num_hosts'] = random.randint( 5 * switch_cnt, 20 * switch_cnt) self.ctx.config['param_topo_traffic_scale'] = random.randint( 1, 20) * 25 if random.random() > 0.9: # use very high traffic scale for 10% of the scenarios self.ctx.config['param_topo_traffic_scale'] = random.randint( 1, 50) * 250 # only randomize param_topo_switch_capacity if the parameter is not specified if self.ctx.config.get('param_topo_traffic_scale_overwrite') > 0: self.ctx.config['param_topo_traffic_scale'] = ctx.config.get( 'param_topo_traffic_scale_overwrite') # write randomized parameters to disk in case an error occurs (so that # it is possible to reproduce the error) params = [] for k, v in self.ctx.config.items(): if k.startswith('param'): print(k, v) params.append('%s %d\n' % (k, self.ctx.config[k])) # we need access to the generated parameters for scenario selection if k.startswith('param_topo'): self.ctx.statistics['scenario.gen.%s' % k] = v store_params = os.path.join(os.path.dirname(ctx.configfile), 'randomized_params.txt') with open(store_params, 'w') as file: file.write('Randomized params are:\n\n' + ''.join(params)) # create the scenario scenario = DelegationScenario( ctx, **dict( verbose=False, preview_scenario= True, # plot and show scenario, requires verbose=True )) scenario.execute_generator() #scenario.plot_topo() # write statistics scenario.add_data_to_ctx() # debug: plot scenario to file if self.ctx.config.get('param_debug_plot_scenario') == 1: scenario.plot_scenario(show=False) # debug: plot demands if self.ctx.config.get('param_debug_plot_demands') == 1: scenario.plot_demands_of_switch() # indicates that the run generator function from the gui was used, i.e., # no simulation is run (on_simulation_setup_complete etc are not executed) if self.ctx.run_scenario_generator: scenario.plot_scenario(show=True) return # flag was set to only run the scenario generator (similar to above but # can be used in batch mode) if self.ctx.config.get('param_debug_only_run_scenario_generator') == 1: scenario.add_scenario_data_to_statistics() return # add scenario to ctx so it is available for the solvers etc ctx.scenario = scenario if not scenario.threshold > 0: raise Exception("Invalid param_topo_switch_capacity parameter") self.ctx.config['param_topo_switch_capacity'] = scenario.threshold # now pass the scenario over to the RSA algorithm which will then # further call the DTS algorithms self.rss = RSSSolver(ctx) self.rss.run() self.ctx.skip_main_simulation = True # we do not need the usual simulator return