nunobrum / PyLTSpice

Set of tools to interact with LTSpice. See README file for more information.
https://www.nunobrum.com/pyltspice.html
GNU General Public License v3.0
202 stars 59 forks source link

Cannot parse resistors with voltage-based value "R={V(vload)}" #105

Closed junknerd closed 1 year ago

junknerd commented 1 year ago

To create things like dynamic loads, one approach is to set a resistor's value equal a voltage, which LTSpice effectively casts volts --> ohms. To achieve this, the resistor's value must be set as R={V(vload)}, assuming vload was the name of the net with the associated voltage (e.g. 1V becomes 1 ohm).

Presently, this throws an error. I believe line 90 in /sim/spice_editor.py should be changed from: 'R': r"^(?P<designator>R§?\w+)(?P<nodes>(\s+\S+){2})(?P<model>\s+\w+)?\s+(?P<value>({)?(?(6).*}|([0-9\.E+-]+(Meg|[kmuµnpf])?R?)\d*)).*$", # Resistors

to

'R': r"^(?P<designator>R§?\w+)(?P<nodes>(\s+\S+){2})(?P<model>\s+\w+)?\s+(?P<value>(R=)?({)?(?(7).*}|([0-9\.E+-]+(Meg|[kmuµnpf])?R?))).*$", # Resistors

to correct this. Specifically, adding (R=)? then changing the check for group 6 to group 7.

I'm running version 2.1, however the issue seems to still exist in latest version.

nunobrum commented 1 year ago

Hello @junknerd

According to one Spice manual I have found (NGSpice), the resistance format is as follows:

RXXXXXXX n+ n- <resistance|r=>value <ac=val> <m=val>
+ <scale=val> <temp=val> <dtemp=val> <tc1=val> <tc2=val>
+ <noisy=0|1>

The LTSpice manual doesn't even mention this and defines the format as:

Syntax: Rxxx n1 n2 <value> [tc=tc1, tc2, ...] [temp=<value>]

It suggests that the r= is an alternative notation to simply setting the resistance value. In this case, the regular expression should be such as to leave the R=outside of the value update. Here is my proposition:

"^(?P<designator>R§?\w+)(?P<nodes>(\s+\S+){2})(?P<model>\s+\w+)?\s+(R=)?(?P<value>({)?(?(7).*}|([0-9\.E+-]+(Meg|[kmuµnpf])?R?)\d*)).*$"

What do you think ? Anyway... by looking at the documentation, I perceived yet another limitation. Sometime the model can appear after the value, and not before, as is implemented. This regex approach needs to be rethought. I think that eventually, the spice editor will have to become a language parser.

junknerd commented 1 year ago

@nunobrum thanks for looking into this. It is an undocumented feature (https://ltwiki.org/index.php?title=Undocumented_LTspice#Resistors), but seems to be widely used for simulating loads. I primarily use it to simulate short-circuits in a continuous manner. Typically a switch causes an abrupt change that significantly slows or faults simulations.

The R= needs to remain as part of the value if the value is set from a voltage, Thinking through it further, dropping the R= from the value probably fits the most common use cases. If the library also always prepends resistances with R= when writing them, then I think every such case is safely handled.

Here I imagine the most common use case is reading a (numerical) value, updating it, and writing it back out. In this case, something like R=5 might cause confusion if the higher level script doesn't check for the R= and tries to do something like add 10 ohms to the value. Writing a value of R=15 back out should be totally compliant.

For a part where the resistance is set from a voltage, and R= is dropped, the updated value would be broken since a value of R={V(some_voltage_net) + 10} works but a value of {V(some_voltage_net) + 10} does not. If on writing an updated resistor value the library (or user) always prepends R=, then this shouldn't be an issue. I realize the user would also need to understand that {...} exists and not trivially do value = value + 5.

If the library notices a usage like this, and throws a "hey, be aware!" type message, I think that would cover anyone. If it automatically writes resistances with R= prepended, it's probably even more full-proof.

Thanks

nunobrum commented 1 year ago

Hi there @junknerd ,

OK! I understand that we need to keep the R=. Also, I think it makes sense to keep it inside the Value group as you were doing in your first proposal. This way, you can update the resistor value using for example: netlist.set_component_value('R1', 'R = limit(1,100k,V(1,2)*I(V1))')

Let me know if you have any issues with this approach. If not, I'll update the code with your change. Best Regards, Nuno

nunobrum commented 1 year ago

Hello @junknerd I've must uploaded a 4.0.6 version that supports the R= tag on defining the value. Please feel free to reopen this issue if you find any other issues. Best regards, Nuno