Skip to content

Conversation

damiano-flex
Copy link
Contributor

@damiano-flex damiano-flex commented Sep 10, 2025

Add SSAC Voltage source class.

Greptile Summary

Updated On: 2025-09-10 09:22:23 UTC

This PR adds support for Small Signal AC (SSAC) voltage sources to the Tidy3D simulation framework. The implementation introduces a new SSACVoltageSource class that enables AC analysis in heat-charge simulations by providing DC bias voltages, AC frequencies, and small-signal amplitudes.

The core change is the addition of the SSACVoltageSource class in a new module (tidy3d/components/spice/sources/ac.py), which follows the established patterns from the existing DCVoltageSource class. The class includes three main parameters: voltage (array of DC bias voltages), frequency (array of AC analysis frequencies), and amplitude (small-signal AC magnitude). All fields include proper validation to ensure finite values and positive frequencies.

The implementation integrates seamlessly with the existing SPICE sources framework by updating the VoltageSourceType union to include the new AC source alongside the existing DC source. A validation constraint has been added to the HeatChargeSimulation class to enforce that only a single AC source can be present in a simulation's boundary conditions, which prevents ambiguous analysis scenarios.

The new functionality is exposed through the main package interface by adding the class to the imports and __all__ list in tidy3d/__init__.py. This follows the established pattern where SPICE-like analysis components are made available as part of the public API.

Important Files Changed

Changed Files
Filename Score Overview
tidy3d/components/spice/sources/ac.py 4/5 New file implementing SSACVoltageSource class for small-signal AC analysis with proper validation
tidy3d/components/tcad/simulation/heat_charge.py 4/5 Added validator to enforce single AC source constraint in simulations
tests/test_components/test_heat_charge.py 5/5 Added comprehensive validation tests for SSACVoltageSource and multiple AC source constraints
tidy3d/components/spice/sources/types.py 5/5 Updated VoltageSourceType union to include new SSACVoltageSource
tidy3d/__init__.py 5/5 Added SSACVoltageSource to main package imports and exports

Confidence score: 4/5

  • This PR is safe to merge with minimal risk as it adds new functionality without modifying existing behavior
  • Score reflects well-structured implementation following established patterns, though the single AC source constraint may need documentation
  • Pay close attention to tidy3d/components/tcad/simulation/heat_charge.py to ensure the validation logic is complete

Sequence Diagram

sequenceDiagram
    participant User
    participant HeatChargeSimulation
    participant VoltageBC
    participant SSACVoltageSource
    participant ValidationEngine

    User->>HeatChargeSimulation: "Create simulation with VoltageBC"
    HeatChargeSimulation->>VoltageBC: "Initialize VoltageBC"
    VoltageBC->>SSACVoltageSource: "Initialize SSACVoltageSource"
    SSACVoltageSource->>ValidationEngine: "Validate voltage array"
    ValidationEngine-->>SSACVoltageSource: "Check for infinite values"
    ValidationEngine-->>SSACVoltageSource: "Validation result"
    SSACVoltageSource-->>VoltageBC: "Source initialized"
    VoltageBC-->>HeatChargeSimulation: "Boundary condition created"
    HeatChargeSimulation->>ValidationEngine: "Validate single SSAC source"
    ValidationEngine-->>HeatChargeSimulation: "Check boundary specs for multiple AC sources"
    ValidationEngine-->>HeatChargeSimulation: "Validation result"
    HeatChargeSimulation-->>User: "Simulation created with SSAC voltage source"
Loading

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

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

Reviewing changes made in this pull request

@damiano-flex damiano-flex changed the title Add SSAC voltage source feat: Add SSAC voltage source Sep 10, 2025
Notes
-----
- The bias `voltage` refer to the DC operating point above the simulation ground.
Copy link
Collaborator

Choose a reason for hiding this comment

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

It should be RST syntax here

 ``voltage``

As a rule, we don't use markdown notation in the docs. It's either RST (two "`"-s) in docstrings, or single quotes ' in warning/error messages that will be printed to stdout.

voltage: ArrayFloat1D = pd.Field(
...,
title="DC Bias Voltages",
description="List of DC operating point voltages (above ground) used with 'VoltageBC'.",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Also double ` here as it gets converted to a docstring.

-----
- The bias `voltage` refer to the DC operating point above the simulation ground.
- The `frequency` array lists the AC analysis frequencies (in Hz).
- The `amplitude` is the small-signal AC magnitude (in Volts).
Copy link
Collaborator

Choose a reason for hiding this comment

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

Seems like the "frequency" and "amplitude" Note are not really needed given that the information is also given in the field description.

frequency: ArrayFloat1D = pd.Field(
...,
title="AC Analysis Frequencies",
description="List of small-signal analysis frequencies (in Hz).",
Copy link
Collaborator

Choose a reason for hiding this comment

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

No need to say the units in the description if they are defined in the units argument below, as they should be. See for example how this is parsed for the existing VoltageBC:

image

ac_current_voltage: Optional[FreqPotentialDataArray] = pd.Field(
None,
title="AC current vs voltage and frequency",
description="Complex small-signal current I(v, f) as a FreqPotentialDataArray.",
Copy link
Collaborator

Choose a reason for hiding this comment

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

Again, no need to say as a FreqPotentialDataArray here. Since the field type is Optional[FreqPotentialDataArray], this information will already appear in the parsed docstring, including a clickable link to the type.

bc.condition.source.amplitude,
)
raise SetupError(
"'HeatSimulation' does not include any `SSACVoltageSource` in 'boundary_spec')."
Copy link
Collaborator

Choose a reason for hiding this comment

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

' everywhere in error messages.

@pd.validator("amplitude")
def validate_amplitude(cls, val):
if val == td_inf:
raise ValueError(f"Small signal amplitude must be finite. Current amplitude={val}.")
Copy link
Collaborator

Choose a reason for hiding this comment

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

Thinking out loud here, we want the amplitude to not just be finite, but "small" in some sense. Is there something that we can test it against and at least issue a warning if it doesn't seem to be the case?

Copy link
Contributor

Choose a reason for hiding this comment

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

For this type of analysis the perturbed output varies linearly (i.e., perturbation n, p, phi) with the amplitude so in principle in doesn't matter the amplitude. A different story is whether the solution is physically meaningful. I think it is, in general, not straightforward to define a validity limit and it may indeed be the case that it is case-dependent.

It won't hurt, however, to include some warning or note in the docstring mentioning the validity of the simulation.

Copy link
Contributor

@marc-flex marc-flex left a comment

Choose a reason for hiding this comment

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

Thanks @damiano-flex

I left a few comments but this is looking good!

I guess a more general comment, Do we want to include field output? Such as maybe perturbation current, perturbation potential etc?

@pd.validator("amplitude")
def validate_amplitude(cls, val):
if val == td_inf:
raise ValueError(f"Small signal amplitude must be finite. Current amplitude={val}.")
Copy link
Contributor

Choose a reason for hiding this comment

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

For this type of analysis the perturbed output varies linearly (i.e., perturbation n, p, phi) with the amplitude so in principle in doesn't matter the amplitude. A different story is whether the solution is physically meaningful. I think it is, in general, not straightforward to define a validity limit and it may indeed be the case that it is case-dependent.

It won't hurt, however, to include some warning or note in the docstring mentioning the validity of the simulation.

Comment on lines 535 to 547
class FreqPotentialDataArray(DataArray):
"""Frequency-domain array.
Example
-------
>>> f = [2e14, 3e14]
>>> v = [0.1, 0.2, 0.3]
>>> coords = dict(f=f, v=v)
>>> fd = FreqPotentialDataArray((1+1j) * np.random.random((2, 3)), coords=coords)
"""
Copy link
Contributor

Choose a reason for hiding this comment

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

Do we want FreqPotentialDataArray or FreqVoltageDataArray? I think it'd be more consistent with the other classes with voltage

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fair enough.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I vaguely remember this was already introduced for RF hmm

Copy link
Collaborator

Choose a reason for hiding this comment

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

Fyi

class VoltageFreqDataArray(VoltageArray, FreqDataArray):
"""Voltage data array in frequency domain.
Example
-------
>>> import numpy as np
>>> f = [2e9, 3e9, 4e9]
>>> coords = dict(f=f)
>>> data = np.random.random(3) + 1j * np.random.random(3)
>>> vfd = VoltageFreqDataArray(data, coords=coords)
"""
__slots__ = ()

Copy link
Collaborator

Choose a reason for hiding this comment

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

This was all recently added fyi

class VoltageArray(DataArray):
# Always set __slots__ = () to avoid xarray warnings
__slots__ = ()
_data_attrs = {"units": VOLT, "long_name": "voltage"}
class CurrentArray(DataArray):
# Always set __slots__ = () to avoid xarray warnings
__slots__ = ()
_data_attrs = {"units": AMP, "long_name": "current"}
class ImpedanceArray(DataArray):
# Always set __slots__ = () to avoid xarray warnings
__slots__ = ()
_data_attrs = {"units": OHM, "long_name": "impedance"}
# Voltage arrays
class VoltageFreqDataArray(VoltageArray, FreqDataArray):
"""Voltage data array in frequency domain.
Example
-------
>>> import numpy as np
>>> f = [2e9, 3e9, 4e9]
>>> coords = dict(f=f)
>>> data = np.random.random(3) + 1j * np.random.random(3)
>>> vfd = VoltageFreqDataArray(data, coords=coords)
"""
__slots__ = ()
class VoltageTimeDataArray(VoltageArray, TimeDataArray):
"""Voltage data array in time domain.
Example
-------
>>> import numpy as np
>>> t = [0, 1e-9, 2e-9, 3e-9]
>>> coords = dict(t=t)
>>> data = np.sin(2 * np.pi * 1e9 * np.array(t))
>>> vtd = VoltageTimeDataArray(data, coords=coords)
"""
__slots__ = ()
class VoltageFreqModeDataArray(VoltageArray, FreqModeDataArray):
"""Voltage data array in frequency-mode domain.
Example
-------
>>> import numpy as np
>>> f = [2e9, 3e9]
>>> mode_index = [0, 1]
>>> coords = dict(f=f, mode_index=mode_index)
>>> data = np.random.random((2, 2)) + 1j * np.random.random((2, 2))
>>> vfmd = VoltageFreqModeDataArray(data, coords=coords)
"""
__slots__ = ()
# Current arrays
class CurrentFreqDataArray(CurrentArray, FreqDataArray):
"""Current data array in frequency domain.
Example
-------
>>> import numpy as np
>>> f = [2e9, 3e9, 4e9]
>>> coords = dict(f=f)
>>> data = np.random.random(3) + 1j * np.random.random(3)
>>> cfd = CurrentFreqDataArray(data, coords=coords)
"""
__slots__ = ()
class CurrentTimeDataArray(CurrentArray, TimeDataArray):
"""Current data array in time domain.
Example
-------
>>> import numpy as np
>>> t = [0, 1e-9, 2e-9, 3e-9]
>>> coords = dict(t=t)
>>> data = np.cos(2 * np.pi * 1e9 * np.array(t))
>>> ctd = CurrentTimeDataArray(data, coords=coords)
"""
__slots__ = ()
class CurrentFreqModeDataArray(CurrentArray, FreqModeDataArray):
"""Current data array in frequency-mode domain.
Example
-------
>>> import numpy as np
>>> f = [2e9, 3e9]
>>> mode_index = [0, 1]
>>> coords = dict(f=f, mode_index=mode_index)
>>> data = np.random.random((2, 2)) + 1j * np.random.random((2, 2))
>>> cfmd = CurrentFreqModeDataArray(data, coords=coords)
"""
__slots__ = ()
# Impedance arrays
class ImpedanceFreqDataArray(ImpedanceArray, FreqDataArray):
"""Impedance data array in frequency domain.
Example
-------
>>> import numpy as np
>>> f = [2e9, 3e9, 4e9]
>>> coords = dict(f=f)
>>> data = 50.0 + 1j * np.random.random(3)
>>> zfd = ImpedanceFreqDataArray(data, coords=coords)
"""
__slots__ = ()
class ImpedanceTimeDataArray(ImpedanceArray, TimeDataArray):
"""Impedance data array in time domain.
Example
-------
>>> import numpy as np
>>> t = [0, 1e-9, 2e-9, 3e-9]
>>> coords = dict(t=t)
>>> data = 50.0 * np.ones_like(t)
>>> ztd = ImpedanceTimeDataArray(data, coords=coords)
"""
__slots__ = ()
class ImpedanceFreqModeDataArray(ImpedanceArray, FreqModeDataArray):
"""Impedance data array in frequency-mode domain.
Example
-------
>>> import numpy as np
>>> f = [2e9, 3e9]
>>> mode_index = [0, 1]
>>> coords = dict(f=f, mode_index=mode_index)
>>> data = 50.0 + 10.0 * np.random.random((2, 2))
>>> zfmd = ImpedanceFreqModeDataArray(data, coords=coords)
"""
__slots__ = ()

Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks @daquinteroflex!
Last time I spoke to @dmarek-flex we kind of reserved "voltage" to whenever we're comparing potentials and "potential" when we talk about the field. But maybe @dmarek-flex can confirm

Copy link
Contributor

@dmarek-flex dmarek-flex Sep 19, 2025

Choose a reason for hiding this comment

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

yea, same units but conceptually fairly different. Not a strong feeling, but I do think that FreqPotentialDataArray is a better description that also differentiates it more from the VoltageArrays we introduced in RF.

Sadly if we don't modify we will have FreqVoltageDataArray and VoltageFreqDataArray 😞

Copy link
Contributor

@dmarek-flex dmarek-flex Sep 19, 2025

Choose a reason for hiding this comment

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

We still don't have an enforced naming scheme for DataArray, but one idea is to have the leading term prefix relate to the quantity (Voltage/Current/Potential) and the remaining terms relate to the coordinates (Freq/Mode/Spatial). So maybe follow that approach

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually I might be confused about the purpose of FreqVoltageDataArray. The coordinates are frequency and voltage? In that case, I agree with the current approach 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, coordinates are frequency and voltage.

This source represents a small-signal AC excitation defined by a list of bias voltages,
a set of small-signal analysis frequencies, and a small-signal amplitude (linear scale).
The bias ``voltage`` refer to the DC operating point above the simulation ground. Currently, electrical ports are not defined.
Copy link
Contributor

Choose a reason for hiding this comment

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

  • refer -> refers
  • We don't really define a ground, do we? We define a voltage difference. Or have we changed this for SSAC?

Copy link
Contributor Author

@damiano-flex damiano-flex Sep 18, 2025

Choose a reason for hiding this comment

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

Yes, I had a discussion with @daquinteroflex about this. We need to discuss about on how and where let the user set the ground.

voltage: ArrayFloat1D = pd.Field(
...,
title="DC Bias Voltages",
description="List of DC operating point voltages (above ground) used with :class:`VoltageBC`.",
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 this is related to my previous question. Do we define a ground in SSAC?

bc.condition.source.amplitude,
)
raise SetupError(
"'HeatSimulation' does not include any 'SSACVoltageSource' in 'boundary_spec')."
Copy link
Contributor

Choose a reason for hiding this comment

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

HeatSimulation is deprecated. We should use HeatChargeSimulation instead

Comment on lines 1935 to 1951
def _get_voltage_regime(self):
for bc in self.boundary_spec:
if isinstance(bc.condition, VoltageBC):
if isinstance(bc.condition.source, SSACVoltageSource):
return "ac"
return "dc"
Copy link
Contributor

Choose a reason for hiding this comment

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

I think we could generalize this to something like _get_charge_type().
Right now we only need to modify the name while we keep the body of the function but I can see how this can be also helpful as we move into transient (time-domain) simulations.

@marc-flex marc-flex force-pushed the marc/non_isothermal branch 3 times, most recently from aae214d to fe57931 Compare September 18, 2025 14:01
Copy link
Collaborator

Choose a reason for hiding this comment

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

Looks good re what we discussed
Screenshot from 2025-09-18 11-44-15

I guess for this type of analysis we're not specifying the type of AC signal shape?
Screenshot from 2025-09-18 11-47-26

and so you're not envisioning adding a type? Or should we introduce signal shape types even if say this defaults to sine?

Comment on lines 46 to 63
frequency: ArrayFloat1D = pd.Field(
...,
title="AC Analysis Frequencies",
description="List of small-signal analysis frequencies.",
units=HERTZ,
)

amplitude: pd.FiniteFloat = pd.Field(
...,
title="Small-Signal Amplitude",
description="Small-signal AC amplitude.",
units=VOLT,
)
Copy link
Collaborator

Choose a reason for hiding this comment

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

So yeah wondering if we should specify this via something like

class SinusodialSignalType
    frequency: ArrayFloat1D = pd.Field(
        ...,
        title="AC Analysis Frequencies",
        description="List of small-signal analysis frequencies.",
        units=HERTZ,
    )

    amplitude: pd.FiniteFloat = pd.Field(
        ...,
        title="Small-Signal Amplitude",
        description="Small-signal AC amplitude.",
        units=VOLT,
    )

and then:

class ACVoltageSource(Tidy3dBaseModel):

... 

 signal_parameters: SignalType

Just hacked this quickly but maybe also need to think about names

Apart from this rest of the PR looks good

Copy link
Collaborator

Choose a reason for hiding this comment

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

Feel this would be pretty useful for the future too, also suspect maybe we already anyways have somthing like this for some time domain sources used elsewhere in the API

Copy link
Collaborator

Choose a reason for hiding this comment

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

I think that makes some sense @daquinteroflex, similarly to how we have our FDTD sources take a source_time which just defines the time dependence of the source, while the other parameters are related to the spatial extent, specific details of the modes, etc. On the other hand, doesn't amplitude make more sense to be an ACVoltageSource parameter than a SinusoidalSignal (because it's nice if the signal itself is dimensionless). But then that just leaves frequency in the SinusoidalSignal, so it makes it a bit pointless?

I don't think there's anything from the existing API we can reuse directly. One other thing I'm thinking about is that while this is technically a source, it also more closely matches the FDTD monitor-s. You can define a list of frequencies, and you will get output data that has a dimension over that list. This is exactly like our MonitorData. One point here is that we've been already inconsistent about the frequency labeling: we have Monitor.freqs, but the MonitorData has a corresponding f dimension. Shall we at least call frequency -> freqs here to be in line with our FDTD monitors?

Copy link
Collaborator

@daquinteroflex daquinteroflex Sep 19, 2025

Choose a reason for hiding this comment

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

So am thinking eventually we probably want to implement all these different ways of specifying time domain SPICE signal sources (either for voltage or current).

But feel maybe we need to be more specific whether we want to use an ACVoltageSource or simply all the specific signal shapes we want to support directly. I think @damiano-flex made it this way because of the way voltage sources are specified in SPICE

VXXXXXXX N+ N- <<DC> DC/TRAN VALUE> <AC <ACMAG <ACPHASE>>>
+ <DISTOF1 <F1MAG <F1PHASE>>> <DISTOF2 <F2MAG <F2PHASE>>>

translates to

name positive_node negative_node
[this specifies a DC bias <<DC> DC/TRAN VALUE>]
[this specifies a custom AC source <AC <ACMAG <ACPHASE>>> + <DISTOF1 <F1MAG <F1PHASE>>> <DISTOF2 <F2MAG <F2PHASE>>>]
image

So in this sense feeling this is why a ACSignalType should be defined as a standalone class. See 4.1 in https://ngspice.sourceforge.io/docs/ngspice-44-manual.pdf These are some examples of how they can be defined:

Based on these types of signal definitions, in my opinion I actually do think ampltude does make sense to live say in SinusosidalSignal (not my favorite name yet just to be consistent), because say a PulseSignal can be specifed between two voltages rather than just a single magnitude for example, and other AC sources require different parameters independent of amplitude too yet are still "AC" source compatible.

Sounds good, was not sure either what exactly was reusable. I do agree for consistency we can use freqs.

Screenshot from 2025-09-19 11-08-33 Screenshot from 2025-09-19 11-08-23 Screenshot from 2025-09-19 11-08-09

Copy link
Contributor Author

Choose a reason for hiding this comment

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

OK to use freqs for consistency.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Ahh yes, sounds good.

Those are definitely related to what we're doing here, but I was saying that I don't think our existing API can be used, because there's still some assumptions there that this is geared towards EM simulations. For example, the ContinuousWave class is basically a Sinusoidal, but, first of all, it's complex-valued, and second of all, it includes a linear ramp-up based on the fwidth parameter. For something as simple as a base amplitude and an offset amplitude where the source is something like V0 + V1*Sinusoidal, it still makes sense to me to follow this approach of defining a time-only class. I
That said, on second thought, if we do decide to separate time-only (unitless) definitions, this file could be a good place to put some new classes.

This seems a good approach in terms of API consistency, basically these abstract time classes would be an equivalent representation, and we will be able to translate to SPICE types if we need eventually too. Also worth getting @nikoladjor-flex input here. So creating those abstract time-shared classes seems good, and they can be used within ACVoltageSource and or eventually ACCurrentSource too in this approach. I do think it makes sense to define signal classes separate from the kind of physical quantity like Voltage that they modulate for reusability. Not sure what you think about that @damiano-flex ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@daquinteroflex as we discussed in Slack, I don't have a strong opinion about what's the right thing to do. Here's what i think though.

Having a class SinusoidalSignal is not something inherently wrong to me, but providing a vector of frequencies to this class is. Classes in time.py are made to generate a single time-signal each, either by specifying some parameters (ContinousWave) or by specifying the value of the signal in time domain. Providing a vector of frequencies here still seems a bit off to me.

Copy link
Collaborator

@daquinteroflex daquinteroflex Sep 19, 2025

Choose a reason for hiding this comment

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

I agree, a vector would be weird in this case since it doesn't make sense physical sense unless it is a collection of signals. Feel this is a byproduct of wanting to integrate a sweep at the same time as a source potentially in the initial implementation, maybe instead of separating it into the analysis. Personally of the opinion parameter sweeps analysis to be separate from the source definition anyway

Copy link
Contributor

Choose a reason for hiding this comment

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

That's a good point @daquinteroflex. Maybe we could allow to have an array of ac sources?

Choose a reason for hiding this comment

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

Hey, the things I can add at the moment are more related to the application and I might be completely off-topic and wrong:

  • small signal analysis is usually done to determine frequency behaviour when you do not know the impedance of the device and want to get the bandwidth. Two most obvious applications for PIC are p/n modulators and photodetectors. Having array of sources here might help if there is a step in the simulation with brings the system to a steady state and then you apply small signals separately.
  • from the API point of view, this analysis makes sense on a single component, because you want three terminals the most. I tend to agree with @daquinteroflex about the abstract ACSource with added offset and frequency as list maybe. The point with the array of sources is the same as above. My main remark is that from the point of view of setting this analysis up, one needs to know which structures get what type of signal, and if we are already using BCs for that, it's fine, but we need to provide easy way to go from the circuit to the BC definition and from analysis result to impedance.

... )
"""

name: Optional[str]
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is the purpose of the name?

If it's needed, it should be formatted similarly to e.g. this

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I had no purposes for it in mind, I just used the same structure of DCVoltageSource (which has a field formatted like this one) . But in principle, having a name could be useful for future usage (e.g. a function that modify a source value through the name).

So, either I remove it in both, or I format it correctly here and in DCVoltageSource well.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yeah, let's format both, thanks.

@momchil-flex
Copy link
Collaborator

@damiano-flex @marc-flex I am getting confused with the source types and the analysis types. Would one still be defining e.g. IsothermalSteadyChargeDCAnalysis when there are AC type sources?

@damiano-flex
Copy link
Contributor Author

@damiano-flex @marc-flex I am getting confused with the source types and the analysis types. Would one still be defining e.g. IsothermalSteadyChargeDCAnalysis when there are AC type sources?

Initially, I had a different Analysis type for AC analysis. It was virtually a clone. Marc suggested to keep old analysis types, and define AC at source level, and this idea seemed good to me as well. If you have an AC source, you'll get everything from the DC simulation + a graph for AC current.

An option might be that IsothermalSteadyChargeDCAnalysis drops the DC keyword or change its name, since: 1) the DC analysis will be always performed 2) the AC analysis will be performed after the DC analysis if an AC source has been defined.

@momchil-flex
Copy link
Collaborator

@damiano-flex @marc-flex I am getting confused with the source types and the analysis types. Would one still be defining e.g. IsothermalSteadyChargeDCAnalysis when there are AC type sources?

Initially, I had a different Analysis type for AC analysis. It was virtually a clone. Marc suggested to keep old analysis types, and define AC at source level, and this idea seemed good to me as well. If you have an AC source, you'll get everything from the DC simulation + a graph for AC current.

An option might be that IsothermalSteadyChargeDCAnalysis drops the DC keyword or change its name, since: 1) the DC analysis will be always performed 2) the AC analysis will be performed after the DC analysis if an AC source has been defined.

I think that makes sense. Note that, for backwards compatibility, we'd keep IsothermalSteadyChargeDCAnalysis but issue a deprecation warning when using it. So something like

class IsothermalSteadyChargeAnalysis():
    # takes everything that's currently in IsothermalSteadyChargeDCAnalysis

class IsothermalSteadyChargeDCAnalysis(IsothermalSteadyChargeAnalysis):
    # add deprecation warning

Refer to the HeatSimulation.

@marc-flex marc-flex force-pushed the marc/non_isothermal branch 2 times, most recently from 7013f01 to 6c070c7 Compare September 19, 2025 12:28
@damiano-flex damiano-flex force-pushed the damiano/ssac_analysis branch 2 times, most recently from e19ba56 to 02f85c4 Compare September 19, 2025 13:07
@marc-flex
Copy link
Contributor

@damiano-flex @momchil-flex I'm now having second thoughts about the current naming of sources and analysis types.

Right now we're saying that if we see an ACVoltageSource in a boundary condition we'll perform an SSAC simulation. But what if, in the future we can do non-linear simulations with AC sources? I guess we have two way forward:

  1. We create small-signal equivalents of the SteadyChargeAnalysis and IsothermalSteadyChargeAnalysis as @damiano-flex suggested in the first place; or
  2. We create SSACVoltageSource. I guess the only issue with this one is that it isn't very SPICE like

@daquinteroflex
Copy link
Collaborator

These type of charge simulations are different from FDTD simulations in the sense that a given "scene" with sources etc. can have different types of analysis depending on what you're looking into like transient or DC operating point, and so the data generated is inherently different per analysis type.

In any case:

We create small-signal equivalents of the SteadyChargeAnalysis and IsothermalSteadyChargeAnalysis as @damiano-flex suggested in the first place; or

would be then equivalent to this type of SS ac SPICE analysis directive that Damiano mentioned separately, but then the decision is whether the analysis type has the frequency sweep or the source. IMO it should be the analysis but use the source name to reference to it, and override any specified frequency in it.

.ac dec nd fstart fstop
.ac oct no fstart fstop
.ac lin np fstart fstop

Base automatically changed from marc/non_isothermal to develop September 19, 2025 13:43
Copy link
Contributor

Diff Coverage

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

  • tidy3d/init.py (100%)
  • tidy3d/components/data/data_array.py (100%)
  • tidy3d/components/spice/sources/ac.py (96.6%): Missing lines 84
  • tidy3d/components/spice/sources/dc.py (100%)
  • tidy3d/components/spice/sources/types.py (100%)
  • tidy3d/components/tcad/data/sim_data.py (100%)
  • tidy3d/components/tcad/simulation/heat_charge.py (58.3%): Missing lines 1947-1951,1954-1957,1961

Summary

  • Total: 62 lines
  • Missing: 11 lines
  • Coverage: 82%

tidy3d/components/spice/sources/ac.py

Lines 80-86

  80 
  81     @pd.validator("amplitude")
  82     def validate_amplitude(cls, val):
  83         if val == td_inf:
! 84             raise ValueError(f"Small signal amplitude must be finite. Current amplitude={val}.")
  85         return val

tidy3d/components/tcad/simulation/heat_charge.py

Lines 1943-1964

  1943 
  1944         return any(isinstance(source, HeatFromElectricSource) for source in self.sources)
  1945 
  1946     def _get_charge_type(self):
! 1947         for bc in self.boundary_spec:
! 1948             if isinstance(bc.condition, VoltageBC):
! 1949                 if isinstance(bc.condition.source, ACVoltageSource):
! 1950                     return "ac"
! 1951         return "dc"
  1952 
  1953     def _get_ssac_frequency_and_amplitude(self):
! 1954         for bc in self.boundary_spec:
! 1955             if isinstance(bc.condition, VoltageBC):
! 1956                 if isinstance(bc.condition.source, ACVoltageSource):
! 1957                     return (
  1958                         bc.condition.source.freqs.tolist(),
  1959                         bc.condition.source.amplitude,
  1960                     )
! 1961         raise SetupError(
  1962             "'HeatChargeSimulation' does not include any 'ACVoltageSource' in 'boundary_spec')."
  1963         )

@momchil-flex
Copy link
Collaborator

@damiano-flex @momchil-flex I'm now having second thoughts about the current naming of sources and analysis types.

Yeah I don't really know the answer but we must 100% think about it right now before things are released. It does seem to me that the current approach will be becoming more and more tedious/confusing as new regimes are added.

Maybe try to sketch what other analysis we want to support and how what the API might look like - even if it's for one or two years from now. Maybe let's take the discussion off this repo, too.

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.

6 participants