Skip to main content

loopkit

A Python library for modeling, analyzing, and simulating feedback control loops, with a focus on scientific and engineering applications such as laser locking, electronics, and precision instrumentation.


🔧 What is loopkit?

loopkit provides an object-oriented framework for building and analyzing control loops as directed graphs of signal-processing components. It supports:

  • Modular design of control systems using Python classes and NumPy/Scipy tools
  • Construction of linear time-invariant (LTI) models via symbolic transfer functions or tabulated data
  • Frequency-domain and time-domain simulation of system behavior
  • Visualization and analysis of loop stability, gain, noise propagation, and performance
  • Extensibility through subclassing and dynamic graph generation

The library is designed with scientific control systems in mind—especially applications in optics, interferometry, and laser metrology.


🧩 Key Features

  • Component-based modeling: Each system block is a Component with defined transfer functions, inputs, and outputs.
  • Flexible interconnection: Loops are defined by connecting components into directed graphs, not just serial chains.
  • Simulation support: Built-in methods to simulate step responses, noise propagation, and open/closed-loop behavior.
  • Plotting utilities: Visualize Bode plots, noise budgets, signal flow graphs, and more.
  • Extensible: Easily define custom blocks, auto-generate loop structures, and integrate with hardware configurations.

📦 Installation

pip install loopkit

For block diagram generation (loop.block_diagram()), install the optional dependencies:

pip install loopkit[diagram]

Note: pytikz is required for block diagrams but is not on PyPI. Install it from the allefeld fork:

pip install git+https://github.com/allefeld/pytikz.git

🚀 Quick Start

Example

import numpy as np
import matplotlib.pyplot as plt
from loopkit.component import Component
from loopkit.components import PIControllerComponent
from loopkit.loop import LOOP
import loopkit.loopmath as lm

# Define loop parameters
sps = 80e6  # Loop update frequency in Hz
frfr = np.logspace(np.log10(1e0), np.log10(40e6), int(1e5))[:-1]  # Frequency array (Hz)

# Define Plant using Laplace-domain string (auto-discretized)
w_n = 2 * np.pi * 1e6 # 10 kHz resonance
zeta = 2.0 # damping ratio
plant = Component("Plant", sps=sps, tf=f"{w_n**2} / (s**2 + {2*zeta*w_n}*s + {w_n**2})", domain='s')

# Define Sensor using z-domain string (explicit difference equation)
sensor = Component("Sensor", sps=sps, tf="(0.391 + 0.391*z**-1) / (1 - 0.218*z**-1)", domain='z')

# Compute the P-servo log2 gain from a dB value
p_log2_gain = lm.db_to_log2_gain(15)

# Compute the integrator log2 gains for a certain cross-over frequency with the P-servo
i_log2_gain = lm.gain_for_crossover_frequency(p_log2_gain, sps, 1e5, kind='I')

# Define PI controller component with those gains
controller = PIControllerComponent("Controller", sps=sps, Kp=p_log2_gain, Ki=i_log2_gain)

# Build the loop
loop = LOOP(sps, [plant, sensor, controller], name="My Loop")

ugf, margin = lm.get_margin(loop.Gf(f=frfr), frfr, deg=True, unwrap_phase=True, interpolate=True) # compute UGF and phase margin

print(f"Unity gain frequency = {ugf:.4e} Hz; Phase margin = {margin:.4f} degrees")

# Visualize block diagram
loop.block_diagram(dpi=150)

# Bode plot of open-loop gain
ax = loop.bode_plot(frfr)
ax[0].axvline(x=ugf, ls='--', c='gray')
ax[1].axvline(x=ugf, ls='--', c='gray')
plt.show()

# Nyquist plot of open-loop gain
ax = loop.nyquist_plot(frfr, which='G', logy=True, logx=True, critical_point=True)
plt.show()

🧪 Specialized Loop Implementations

The loopkit.loops subpackage provides pre-built loop models:

  • PLL: Digital phase-locked loop model.
  • MokuLaserLock: Laser frequency lock model for Moku hardware (heterodyne phase-locking).
  • NPROLaserLock: Composite model for NPRO laser frequency stabilization using PZT and temperature control loops with digital PLL (phasemeter) readout.
  • LaserLockPZT, LaserLockTemp: Building blocks for laser lock subsystems.
from loopkit.loops import PLL, MokuLaserLock, NPROLaserLock

📚 Documentation

In-depth API documentation, tutorials, and example notebooks coming soon.


💡 Design Philosophy

  • Favor explicit, graph-based design over implicit signal paths
  • Maintain clarity between loop structure and numerical simulation
  • Support rapid prototyping of complex control systems
  • Blend symbolic (transfer function) and numeric (data-driven) components

👥 Contributing

Contributions are welcome! We are especially interested in:

  • New component definitions (hardware models, filters, sensors)
  • Visualization tools for loop inspection
  • Test coverage and validation scripts
  • Real-world loop configurations from physics labs

📜 License

BSD 3-Clause License.


🛰 Authors & Acknowledgments

Developed by Miguel Dovale, based on an initial implementation by Kohei Yamamoto.


Metadata

Release files for loopkit 1.0.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for loopkit 1.0.1
File Size Uploaded
loopkit-1.0.1.tar.gz 137.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for loopkit 1.0.1
File Interpreter ABI Platform
loopkit-1.0.1-py3-none-any.whl Python 3 none any Details

Total release size: 249.4 kB

Release files / loopkit-1.0.1.tar.gz

Download URL loopkit-1.0.1.tar.gz
Size 137.7 kB
Tags Source
SHA-256 checksum
How to use checksums
abcf0039a2adf0d6116ce72f679a66b6d0c6fb47f70811360781e7efd4440f2d
BLAKE2b-256 checksum
How to use checksums
74143280fb37a55d27d7d2e676eb3ab4349e3e8650b2198b07b88af3bf4f0f2e
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.12

Release files / loopkit-1.0.1-py3-none-any.whl

Download URL loopkit-1.0.1-py3-none-any.whl
Size 111.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
60657619692727d22efcb5ba4566cc82b126317bb011985eb9612099fbc228e1
BLAKE2b-256 checksum
How to use checksums
2e6eba08ed20fdd1c23abeef6e29092e0acb9162d5570a75146103c88cd8a19a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.12

Release history Release notifications | RSS feed

This release

1.0.1 This release

2 release files

1.0.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page