Skip to content

Added ring modulator and test #28

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 4 commits into
base: main
Choose a base branch
from

Conversation

alexsludds
Copy link

@alexsludds alexsludds commented Mar 23, 2025

This pull request adds a ring modulator device that includes accurate models of the time dynamics/optical peaking of the ring.

Values are set to reasonable values based on literature/my experience for rings targeting ~50Gbaud.

Future work would include looking at ways to speedup the ring and adding examples of PAM-4 for a ring modulator.

Summary by Sourcery

Adds a ring modulator device with accurate models of the time dynamics and optical peaking of the ring. Includes parameters for rings targeting ~50Gbaud.

New Features:

  • Introduces a RingModulator class that models the time-domain and frequency-domain response of an optical ring resonator to optical and voltage inputs.
  • Adds functionality to process input waveforms through the resonator, accounting for wavelength offset and voltage modulation.
  • Implements an RC filter for the voltage input to model electrical effects.
  • Provides methods to calculate and plot the frequency response of the resonator.
  • Includes properties to calculate key ring resonator parameters such as FSR, finesse, FWHM, quality factor, and photon lifetime.

Copy link
Contributor

sourcery-ai bot commented Mar 23, 2025

Reviewer's Guide by Sourcery

This pull request introduces a RingModulator class in optic/models/devices.py that models the behavior of an optical ring resonator. The implementation includes methods for calculating the time and frequency domain responses of the ring to both optical and voltage inputs. The class includes parameters for radius, resonant wavelength, effective index, group index, and coupling coefficient. The process_waveform method simulates the ring's response to an input waveform, including the effects of wavelength offset and voltage modulation. An RC filter was also added to the ring modulator.

Sequence diagram for RingModulator.process_waveform

sequenceDiagram
    participant User
    participant RingModulator
    participant RC Filter

    User->>RingModulator: process_waveform(input_waveform, dt, wavelength_offset, voltage_waveform)
    RingModulator->>RingModulator: reset()
    RingModulator->>RingModulator: setup_sampling(dt)
    alt voltage_waveform is not None and rc_filter_enabled
        RingModulator->>RC Filter: Apply RC filter to voltage_waveform
        RC Filter-->>RingModulator: filtered_voltage
    else voltage_waveform is None or rc_filter_enabled is false
        RingModulator->>RingModulator: filtered_voltage = voltage_waveform
    end
    loop for each sample in input_waveform
        RingModulator->>RingModulator: Calculate phase (phi)
        RingModulator->>RingModulator: Get delayed output from buffer
        RingModulator->>RingModulator: Calculate new transmission (T_current)
        RingModulator->>RingModulator: Store T_current in buffer
        RingModulator->>RingModulator: Calculate output
    end
    RingModulator-->>User: output_waveform
Loading

Class diagram for RingModulator

classDiagram
    class RingModulator{
        -radius: float
        -resonant_wavelength: float
        -n_eff: float
        -ng: float
        -dn_dV: float
        -a: float
        -kappa: float
        -buffer_size: int
        -rc_filter_enabled: bool
        -rc_time_constant: float
        -sigma: float
        -Lrt: float
        -junction_loss_dB_m: float
        -tau: float
        -phase_offset: float
        -buffer: numpy.ndarray
        -buffer_idx: int
        -buffer_initialized: bool
        -previous_filtered_voltage: float
        +__init__(
            radius=10e-6,
            resonant_wavelength=1550e-9,
            n_eff=2.4,
            ng=4.2,
            dn_dV=2E-4,
            a=4000,
            kappa=0.1,
            buffer_size=1000000,
            rc_filter_enabled=False,
            rc_time_constant=1e-9
        )
        +reset()
        +FSR
        +finesse
        +FWHM
        +quality_factor
        +photon_lifetime
        +setup_sampling(dt)
        +process_waveform(input_waveform, dt, wavelength_offset=0.0, voltage_waveform=None)
        +calculate_phase(wavelength, voltage=0)
        +plot_frequency_response(lambda_start=1500e-9, lambda_end=1600e-9, points=1000, voltage=0)
    }
    note for RingModulator "Represents an optical ring resonator with bus coupling.
Calculates both the time domain (transient) and frequency domain (steady state) response."
Loading

File-Level Changes

Change Details Files
Introduced a RingModulator class to model an optical ring resonator, including time-domain and frequency-domain responses to optical and voltage inputs.
  • Added an __init__ method to initialize ring resonator parameters such as radius, resonant wavelength, effective index, group index, and coupling coefficient.
  • Implemented reset method to reset the resonator's state.
  • Added properties for calculating Free Spectral Range (FSR), finesse, FWHM, quality factor, and photon lifetime.
  • Implemented setup_sampling to determine the number of samples needed in the buffer for accurate delay representation.
  • Implemented process_waveform to process input waveforms through the resonator, considering wavelength offset and voltage modulation, with an option for an RC filter.
  • Implemented calculate_phase to calculate the phase shift based on wavelength and voltage.
  • Implemented plot_frequency_response to plot the frequency response of the resonator.
  • Added RC filter to the ring modulator to simulate the electrical effects of the modulator.
optic/models/devices.py

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!
  • Generate a plan of action for an issue: Comment @sourcery-ai plan on
    an issue to generate a plan of action for it.

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Contributor

@sourcery-ai sourcery-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @alexsludds - I've reviewed your changes - here's some feedback:

Overall Comments:

  • Consider adding a unit test or example demonstrating the usage of the RingModulator class.
  • It might be helpful to include a diagram or visual representation of the ring modulator to aid understanding.
Here's what I looked at during the review
  • 🟡 General issues: 2 issues found
  • 🟢 Security: all looks good
  • 🟢 Testing: all looks good
  • 🟡 Complexity: 1 issue found
  • 🟢 Documentation: all looks good

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

if buffer_samples > self.buffer_size:
self.buffer = np.zeros(buffer_samples * 2, dtype=complex)
self.buffer_size = buffer_samples * 2
print(f"Warning: Buffer size increased to {self.buffer_size} to accommodate delay")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion: Consider using a logging framework instead of print statements.

For production-level code, using a logging mechanism would allow better control over the verbosity and destination of such messages. This is especially useful for warning messages like buffer size adjustments.

Suggested implementation:

import numpy as np
import logging
            logging.warning(f"Buffer size increased to {self.buffer_size} to accommodate delay")

If your codebase already configures logging elsewhere, ensure that the configuration (log level, handlers, etc.) is compatible with this usage.

resonant_wavelength=1550e-9,
n_eff=2.4,
ng = 4.2,
dn_dV = 2E-4,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick (typo): Documentation mismatch in parameter naming.

The init docstring refers to the effective index parameter with the description 'rseonant wavelength' and also uses 'n_g' in the description instead of 'ng'. Aligning the parameter name and correcting the typo will improve the documentation clarity.

Suggested implementation:

         resonant wavelength : float
             The effective operating wavelength of the resonator in meters.
         ng : float
             The group index of the resonator.

Review the entire init docstring to ensure that all other parameter names match their variable names in the signature.

# Pre-compute voltage scaling factor
voltage_factor = 2*np.pi/self.resonant_wavelength * self.dn_dV * self.Lrt

# Pre-process voltage waveform if RC filter is enabled
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (complexity): Consider using scipy.signal.lfilter for RC filtering and extracting the transmission computation into a helper function to improve code clarity and reduce manual bookkeeping within the waveform processing loop.

Consider simplifying the RC filtering and decoupling state updates from the phase/voltage logic. For example, rather than an explicit loop, you can use signal processing routines like scipy.signal.lfilter for the RC filter:

from scipy.signal import lfilter

# Replace RC filter loop with vectorized filtering:
if voltage_waveform is not None and self.rc_filter_enabled:
    alpha = dt / (self.rc_time_constant + dt)
    # Filter transfer function coefficients for first-order IIR filter
    filtered_voltage = lfilter([alpha], [1, -(1 - alpha)], voltage_waveform)
else:
    filtered_voltage = voltage_waveform

Additionally, consider isolating the transmission computation within the waveform processing loop to reduce coupling. For instance, extract the per-sample calculation into a helper function:

def compute_transmission(self, T_delayed, phi):
    """Compute the new transmission value."""
    return self.sigma + self.a * np.exp(-1j * phi) * (self.sigma * T_delayed - 1)

Then update the loop as follows:

for i in range(n_samples):
    # Calculate phase for this sample
    phi = base_phi
    if voltage_waveform is not None:
        phi += voltage_factor * filtered_voltage[i]

    T_delayed = T_buffer[i]
    T_current = self.compute_transmission(T_delayed, phi)
    T_buffer[i + buffer_samples] = T_current
    output_waveform[i] = input_waveform[i] * T_current

These targeted changes will preserve functionality while reducing manual bookkeeping and improving overall clarity.

@lucasgrjn
Copy link

Hey @alexsludds, that's a great idea and example!

@edsonportosilva
Copy link
Owner

edsonportosilva commented Mar 24, 2025

Hello, @alexsludds! That's great you have a contribution to the repository! :)

Please, note that OptiCommPy is a function-based instead of a class-based framework. The choice for functions instead of classes is to make the use of the code as simple as possible. So, please, I would like to ask you to make the following changes in your code:

  1. Adapt the ring modulator class into a function. The function should receive all the necessary parameters as a parameters object from optic.utils. You can use the function basicLaserModel in optic.models.devices as a reference.

  2. The methods of the class, you can break into functions and add it to the appropriate module, such as optic.dsp.core, etc. If you think there are functions that could be misplaced, given the current organization of the repository, we can discuss if it would make sense to create a module dedicated to optical modulators.

Let me know if you have any questions!

@edsonportosilva edsonportosilva self-assigned this Mar 24, 2025
@alexsludds
Copy link
Author

@edsonportosilva I updated the ring to be a function that takes in parameters. Please take a look!

@edsonportosilva
Copy link
Owner

Hi, @alexsludds. Great job! It seems the model is working fine, at least it ran without problems here. Two things to before we can merge the pull request:

  1. I would appreciate it if you could add at the beginning of the test notebook a brief description of the ring modulator structure, with the basic concepts (model equations?) the user should be aware of upon including the model in a simulation.

  2. I have noticed that changing the sampling rate of the simulation to e.g. 4 samples/symbol affects a lot the output of the ring. Currently, the oversampling in your example is 64, which is quite high considering the kind of system simulations we do with OptiCommPy. Is there a minimum sampling rate the ring modulator model requires? If that is the case, please make sure this information is in the documentation of the function and, perhaps, somewhere in the test notebook.

@edsonportosilva
Copy link
Owner

@alexsludds, another small fix: in the notebook you have created a new class to define the parameters, but instead you just need to instantiate a parameters object. See below:

# Create a parameter structure for the ring modulator
param = parameters()
param.dt = None  # Will be set to Ts later
param.radius = ring_radius
param.resonant_wavelength = ring_resonant_wavelength
param.n_eff = ring_n_eff
param.ng = ring_ng
param.dn_dV = ring_dn_dV
param.loss_dB_m = ring_loss_dB_m
param.kappa_power = ring_kappa_power
param.buffer_size_hint = 1000000
param.rc_filter_enabled = ring_rc_filter_enabled
param.rc_time_constant = ring_rc_time_constant
param.wavelength_offset = -75e-12

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants