scikit-rf Comparison
ParamRF is designed to complement scikit-rf and not replace it, offering a fundamentally different approach to RF modeling. A very brief summary of the differences, strengths and weakness of the libraries is presented below.
Core Philosophy
scikit-rf is built around the static skrf.Network class. While this is excellent for manipulating measurement data (e.g. for calibration) or for generating simple circuits at a given frequency, its structure is such that parameters are simply inputs to functions. For complex, deeply nested architectures, it becomes tedious to manually propagate variables down multiple layers of sub-circuit functions in order to arrive at a final network.
Because of this approach, any analysis or control over parameters, such as deciding which should be fixed, adding constraints for optimization etc., is left to the user. This is not only tedious, but can add lots of overhead (the author knows!) since the control logic is ultimately in Python.
ParamRF is built around the parametric pmrf.Model class. This provides an object-oriented scaffold, where model parameters are stored internally, as opposed to their S-parameter matrix and frequency. Parameter bounds, constraints etc. are then stored alongside these parameters and propagated across any complex hierarchy of models, making it trivial to work with deeply nested models. This makes modular fitting, optimization, and sampling much more convenient.
Concrete Comparison
In scikit-rf, as a consequence of the core class being skrf.Network (which holds its static S-parameters), scikit-rf uses a helper class skrf.Media which defines some propagation media. Transmission lines and components are then created from this media using e.g. res = media.resistor(10) or line = media.line(10, unit='m'). This makes components (such as resistors) feel like “second-class” citizens - the resultant res and line objects in these examples are now just skrf.Network objects storing their S-matrix, and have no access to their original parameters.
The scikit-rf approach is somewhat in contrast to traditional circuit modeling packages, where the user thinks of the resistor itself as the first-class citizen. In ParamRF, components themselves are therefore the core classes (such as Resistor or PhysicalLine) and their S-parameters are simply a result of evaluating the model. This is similar to the approach taken in machine learning. Specifically, the user instantiates model instances directly (using e.g. res = Resistor(10) or line = PhysicalLine(length=10)) and combines them to create new models. The resultant model outputs (e.g. S-parameters) are then lazily evaluated when needed.
This approach not only feels more intuitive, but also allows for more complex modeling, computational simplifications, and model manipulation (i.e. modifying parameters after a model is constructed). Further, since ParamRF builds on top of the jax library (instead of numpy), models can be just-in-time compiled, as well as differentiated with respect to either parameters or frequency using auto-differentiation.
When to use scikit-rf
If your workflow involves standard microwave engineering tasks or static measurements, scikit-rf remains the go-to choice. It excels at:
Data I/O & Calibration: Reading Touchstone files, applying VNA calibrations (TRL, SOLT), de-embedding network data.
Static Network Manipulation: Cascading measurements, port impedance transformations, and standard plotting (e.g., Smith charts).
Static Network Analysis: Analyzing existing networks, such as using time-domain reflectometry or checking networks for passivity/causality.
Flat Circuit Generation: Quickly generating analytical components or simple, flat circuit models where continuous, high-dimensional parameter optimization or analysis is not the primary goal.
When to use ParamRF
If your workflow requires the use of complex or high-dimensional RF models or state-of-the-art optimization techniques, ParamRF provides the architecture to do this both conveniently and efficiently. It excels at:
Deeply Nested Modeling: Building complex, hierarchical models without the boilerplate of manually passing variables through sub-circuit functions.
Differentiability: Using JAX, models can be differentiated with respect to both parameter and frequency.
Analytical and Numerical Modeling: The
pmrf.Modelclass is easy to extend to implement custom equation-based models. Further, since ParamRF integrates with the machine-learning focused libraryEquinox, numerical/surrogate/ML models can be built natively.High-Speed Fitting: Leveraging JAX Just-In-Time (JIT) compilation to fit models to target data is much faster than standard Python loops.
Statistical Analysis: Running automated Monte Carlo or Latin Hypercube sampling across your model’s entire parameter space.
Also note that pmrf.Model instances can easily be converted to skrf.Network objects by simply passing a frequency to pmrf.Model.to_skrf(), allowing, for example, models to be defined and optimized in ParamRF and then further analyzed in scikit-rf.