OPICS Multiprocessing
Contents
OPICS Multiprocessing¶
OPICS support multiprocessing for faster simulations when working with large circuits.
import multiprocessing as mp
import opics as op
import numpy as np
import pandas as pd
import time
____ ____ _______________
/ __ \/ __ \/ _/ ____/ ___/
/ / / / /_/ // // / \__ \
/ /_/ / ____// // /___ ___/ /
\____/_/ /___/\____//____/
OPICS version 0.3.1
Enabling multiprocessing¶
Option 1: Using mp_config
¶
OPICS multiprocessing can be enabled using the mp_config
argument in opics.Network
module.
circuit = op.Network(network_id="MRR_arr", mp_config={"enabled": True, "proc_count": 0, "close_pool": True})
OPICS multiprocessing is enabled.
Option 2: Using enable_mp
¶
OPICS multiprocessing can also be enabled by calling the enable_mp
function in opics.Network
module.
circuit = op.Network(network_id = "MRR_arr")
circuit.enable_mp( process_count = 0,
close_pool = True)
OPICS multiprocessing is enabled.
Disable multiprocessing¶
OPICS multiprocessing can be disabled by calling the disable_mp
function.
circuit.disable_mp()
OPICS multiprocessing is disabled.
Example: Multiple ring resonators coupled to a waveguide¶
In order to see how multiprocessing can help speed-up simulations, let’s create a circuit with n
number of ring resonators coupled to a waveguide.
Without multiprocessing
We will be using bulk_add_component
to add multiple components to the network.
components = op.libraries.ebeam
from opics.network import bulk_add_component
timer_start = time.perf_counter()
circuit = op.Network()
n_rings = 1500
_components_data = []
for count in range(n_rings):
_components_data.append(
{"component": components.DC_halfring,
"params": {"f": circuit.f},
"component_id": f"dc_{count}"})
_components_data.append(
{"component": components.Waveguide,
"params": {"f": circuit.f, "length": np.pi * 5e-6},
"component_id": f"wg_{count}"})
bulk_add_component(circuit, _components_data)
circuit.add_component(components.GC, component_id="input")
circuit.add_component(components.GC, component_id="output")
# bulk connect
prev_comp = ""
for count in range(n_rings):
if count == 0:
circuit.connect("input", 1, f"dc_{count}", 0)
circuit.connect(f"dc_{count}", 1, f"wg_{count}", 0)
circuit.connect(f"wg_{count}", 1, f"dc_{count}", 3)
prev_comp = "dc_0"
elif count >= 1:
circuit.connect(prev_comp, 2, f"dc_{count}", 0)
circuit.connect(f"dc_{count}", 1, f"wg_{count}", 0)
circuit.connect(f"wg_{count}", 1, f"dc_{count}", 3)
prev_comp = f"dc_{count}"
circuit.connect(prev_comp, 2, "output", 1)
circuit.simulate_network()
timer_stop = time.perf_counter()
sim_time = timer_stop - timer_start
print(f"simulation finished in {sim_time} s")
simulation finished in 18.2628713 s
With multiprocessing enabled
timer_start = time.perf_counter()
circuit = op.Network()
circuit.enable_mp()
n_rings = 1500
_components_data = []
for count in range(n_rings):
_components_data.append(
{"component": components.DC_halfring,
"params": {"f": circuit.f},
"component_id": f"dc_{count}"})
_components_data.append(
{"component": components.Waveguide,
"params": {"f": circuit.f, "length": np.pi * 5e-6},
"component_id": f"wg_{count}"})
bulk_add_component(circuit, _components_data)
circuit.add_component(components.GC, component_id="input")
circuit.add_component(components.GC, component_id="output")
# bulk connect
prev_comp = ""
for count in range(n_rings):
if count == 0:
circuit.connect("input", 1, f"dc_{count}", 0)
circuit.connect(f"dc_{count}", 1, f"wg_{count}", 0)
circuit.connect(f"wg_{count}", 1, f"dc_{count}", 3)
prev_comp = "dc_0"
elif count >= 1:
circuit.connect(prev_comp, 2, f"dc_{count}", 0)
circuit.connect(f"dc_{count}", 1, f"wg_{count}", 0)
circuit.connect(f"wg_{count}", 1, f"dc_{count}", 3)
prev_comp = f"dc_{count}"
circuit.connect(prev_comp, 2, "output", 1)
circuit.simulate_network()
timer_stop = time.perf_counter()
sim_time = timer_stop - timer_start
print(f"simulation finished in {sim_time} s")
OPICS multiprocessing is enabled.
simulation finished in 14.118123099999998 s
With multiprocessing enabled, we can get speed-ups of upto 30-40%.
Here are multiprocessing results for different values of n
on an AMD Ryzen 9 5900X 12-Core Processor
.
df = pd.read_pickle("../_static/_data/mp_log_data.pkl")
df
n_count | t_normal (s) | t_mp (s) | speed-up(%) | |
---|---|---|---|---|
0 | 10 | 0.12 | 1.07 | -7.916667 |
1 | 100 | 1.09 | 1.59 | -0.458716 |
2 | 1000 | 11.58 | 8.08 | 0.302245 |
3 | 10000 | 221.40 | 140.95 | 0.363369 |
4 | 20000 | 693.65 | 409.24 | 0.410019 |
5 | 25000 | 1085.94 | 602.27 | 0.445393 |
For smaller circuits, there is a penalty
for creating sub-processes. This is something to keep in mind when using OPICS multiprocessing. However, for large circuits, we observed speed-ups of up to 45%
.