Skip to content

Conversation

@dmarek-flex
Copy link
Contributor

@dmarek-flex dmarek-flex commented Feb 6, 2026

Note

Medium Risk
Introduces new SourceTime variants and updates schema/type unions used broadly in simulation validation, which could affect source serialization/validation and downstream integrations relying on SourceTimeType.

Overview
Adds new baseband (no-carrier) SourceTime options for transient RF workflows: BasebandStep, BasebandGaussianPulse, BasebandRectangularPulse, and BasebandCustomSourceTime (including from_values, end_time, and FFT-based frequency_range).

Plumbs these new source times through the public API (tidy3d.__init__, tidy3d.rf), schema discriminators (Simulation.json, ModeSimulation.json, TerminalComponentModeler.json), and type wiring by moving SourceTimeType into a new components/types/time.py module; also tightens validation by requiring positive dt for CustomSourceTime.from_values and tweaking the low-frequency error message for sources.

Extensive new tests validate baseband behavior (phase locked to 0, amplitude shapes, end_time/frequency_range, plotting, and full Simulation validation) plus CustomSourceTime dt edge cases.

Written by Cursor Bugbot for commit 79ef5bd. This will update automatically on new commits. Configure here.

@dmarek-flex dmarek-flex self-assigned this Feb 6, 2026
@dmarek-flex dmarek-flex added the RF label Feb 6, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Feb 6, 2026

Diff Coverage

Diff: origin/develop...HEAD, staged and unstaged changes

  • tidy3d/init.py (100%)
  • tidy3d/components/microwave/time.py (100%)
  • tidy3d/components/source/base.py (100%)
  • tidy3d/components/source/time.py (88.9%): Missing lines 110,115
  • tidy3d/components/types/time.py (100%)
  • tidy3d/plugins/smatrix/component_modelers/base.py (100%)
  • tidy3d/rf.py (100%)

Summary

  • Total: 149 lines
  • Missing: 2 lines
  • Coverage: 98%

tidy3d/components/source/time.py

Lines 106-119

  106         freqs = np.fft.rfftfreq(len(values), d=dt)
  107 
  108         peak = np.max(spectrum)
  109         if peak == 0:
! 110             return (0.0, 1.0)
  111 
  112         cutoff = np.exp(-(num_fwidth**2) / 2)
  113         above_cutoff = spectrum >= cutoff * peak
  114         if not np.any(above_cutoff):
! 115             return (0.0, freqs[-1])
  116 
  117         indices = np.where(above_cutoff)[0]
  118         fmin = float(freqs[indices[0]])
  119         fmax = float(freqs[indices[-1]])

@dmarek-flex dmarek-flex force-pushed the dmarek/FXC-4546-baseband-source-time branch from 711db32 to 965c864 Compare February 6, 2026 22:01
@dmarek-flex
Copy link
Contributor Author

Here are some nice pictures of the new baseband signals
demo_baseband

@dmarek-flex dmarek-flex force-pushed the dmarek/FXC-4546-baseband-source-time branch from 965c864 to 8e17e32 Compare February 7, 2026 03:25
@dmarek-flex dmarek-flex force-pushed the dmarek/FXC-4546-baseband-source-time branch 2 times, most recently from a75d1be to 222b098 Compare February 8, 2026 23:04
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

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

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Bugbot Autofix is OFF. To automatically fix reported issues with Cloud Agents, enable Autofix in the Cursor dashboard.

@dmarek-flex
Copy link
Contributor Author

This one is basically ready to go. I opted for creating new classes to handles these signals with DC components, instead of modifying the existing ones to support freq0=0, I think the distinction is clearer this way. But open to your thoughts.

Add BasebandStep, BasebandGaussianPulse,
BasebandRectangularPulse, and BasebandCustomSourceTime for
unmodulated transient RF excitations (TDR, step functions,
arbitrary time profiles). These inherit from SourceTime directly,
require no freq0/fwidth, and compute frequency_range from the
signal's actual frequency content.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@dmarek-flex dmarek-flex force-pushed the dmarek/FXC-4546-baseband-source-time branch from 222b098 to 79ef5bd Compare February 10, 2026 18:40
Copy link
Contributor

@George-Guryev-flxcmp George-Guryev-flxcmp left a comment

Choose a reason for hiding this comment

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

Thanks, Damian — this looks solid! I have a few minor comments and suggestions.

title="Rise Time",
description="Characteristic rise time in seconds. "
"The 10%-to-90% rise time is approximately 2.56 times this value.",
)
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe the variable rise_time is misleading in this context: the rise time is typically a time it takes to go from 10% to 90%, while the variable rise_time is used here as sigma in step function below. The actual rise time is approximately 2.56 * sigma. I wonder one can rename rise_time to standard_deviation or something similar? Or alternatively use the actual rise time for this variable.

Image

Comment on lines +28 to +32
The ``phase`` parameter is locked to 0 since baseband signals have no carrier.
"""

phase: Literal[0] = Field(
0,
Copy link
Contributor

Choose a reason for hiding this comment

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

I guess the default nominal value makes sense. However, I wonder if one could have a scenario where a simulation has multiple ports (say two) whose sources/waveforms should have a constant phase offset to each other? One can surely do phase scaling in post-processing, however, I wonder if you think this might be a useful behavior here (i.e. default to 0 but allow for any constant phase offset for a given waveform?)

Comment on lines +70 to +71
"""Equivalent bandwidth in Hz."""
return 1.0 / (2 * np.pi * self.rise_time)
Copy link
Contributor

Choose a reason for hiding this comment

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

I believe this uses the RMS bandwidth; however, multiple alternative definitions exist. Making this explicit in the docstring would remove any ambiguity.

"""Equivalent bandwidth in Hz."""
return 1.0 / (2 * np.pi * self.rise_time)

def amp_time(self, time: float) -> complex:
Copy link
Contributor

Choose a reason for hiding this comment

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

All amp_time() methods give a type hint float for time. However, in most cases time is treated as a numpy array. I wonder you could use time: Union[float, ArrayLike] or something similar?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants