Menu ▾ ▴

#875 .save none sticks: every later analysis keeps vectors at length 1 (shared library)

v1.0 (example)
open-fixed
None
5
2026-10-12
2026-10-04
No

ngspice 47 (also present in pre-master-48 as of 2 Oct 2026), macOS 15, x86_64, built as the shared library (--with-ngshared).

Summary. In outitf.c, .save none sets the file-static savenone, and nothing ever clears it. In the shared library, where one process runs many circuits, every later analysis then keeps its vectors at length 1 (an AC sweep holds only its last frequency). This happens even in a new circuit with no .save line, and neither .save all nor named saves undo it.

Reproduce. The attached savenone-sticks.c runs an AC sweep (41 points), then a transient with .save none, then the same AC sweep again. The second sweep should hold 41 points and holds 1. The program exits 1 when it does.

cc -o savenone-sticks savenone-sticks.c -I<dir of sharedspice.h> -L<dir> -lngspice

Results (points in: AC sweep / transient with .save none / AC sweep again):

stock ngspice 47:                41 / 1 / 1
47 + savenone-sticks.patch:      41 / 1 / 41

Patch (attached, against pre-master-48; also applies to 47). Reset savenone at the start of each new plot, before that plot's saves are read. It's under #ifdef SHARED_MODULE, since the problem only shows when one process runs more than one circuit; it would be harmless without the #ifdef too. A run with .save none still keeps its vectors at length 1, so the streaming use (where .save none stops an endless run storing every point) is unchanged.

2 Attachments

Discussion

  • Steven Franklin

    Steven Franklin - 2026-10-04

    Correction to the patch description: the #ifdef SHARED_MODULE around the reset is required, not optional — savenone itself is only declared under SHARED_MODULE (outitf.c), so the non-shared build wouldn't compile without it. The patch itself is unchanged.

     
  • Giles Atkinson

    Giles Atkinson - 2026-10-04

    It seems to me that the proposed patch is as buggy as before, but with more surprising behaviour. A ".save none" will work for only one run, although it is never overridden.

    I think it makes sense to remove the conditional compilation. "Save none" seems reasonable at any time, but currently gives an error in the program.

     
  • Holger Vogt

    Holger Vogt - 2026-10-04

    I have added savenone to shared ngspice, as the caller may obtain and process the resulting data at each (time) step, and thus there might be no need to store the data within ngspice. Or more precisely: only the data set of the last time point is stored in the vectors of length 1.

    Can we imaginne a use case where this feature is helpful also for the standard executable?

     
  • Holger Vogt

    Holger Vogt - 2026-10-04

    I'd prefer a solution which is not 'automatic' (automagic), but transparent to the user.

    We also have to consider runs which are controlled by a (large) ngspice script, with potentially multiple 'source' and 'save' commands or whatever.

    What about resetting savenone by another .save or save command?

    Are 'save' commands in a script executed where they are located in the script, or are they collected and assembled and run only once? I don't remember, so this has to be checked as well.

     

    Last edit: Holger Vogt 2026-10-04
  • Steven Franklin

    Steven Franklin - 2026-10-04

    Thanks to you both. The use case, as Holger asked: we're a host app using
    shared ngspice. A live run uses ".save none" and takes every point
    through SendData, because nothing stops it and stored vectors would grow
    for ever. Then, in the same process and without reloading the library,
    the user runs an ordinary batch analysis (AC or transient), sources a
    netlist with no .save at all, and reads its vectors. That's where the
    flag left over from the live run gave us vectors of length 1.

    An explicit reset is fine, and I agree it's clearer than our
    patch's automatic one. If a ".save" or "save" command (e.g. ".save all")
    clears savenone, we'll emit one in every batch netlist. The one thing
    I'd ask is that the manual says savenone lasts until a later save
    command, because a netlist with no .save line still runs under it.

    No objection to removing the #ifdef, as Giles suggests.

    I don't know whether save commands in a script run where they appear
    or are collected first, sorry.

     
  • Giles Atkinson

    Giles Atkinson - 2026-10-04

    From the code. it looks simple: just move the reset of savenone into the conditional block a few lines below. That does not work, because the source of the list of save items is the current set of DB structs, that also holds things like plot windows and pending stop commands. It seems that save items might be purged from the list on each new save command, but that does not happen: the list just gets longer. So once "none" is on the list. it stays there and affects all subsequent runs.

    But that behaviour is not actually a bug: it is useful, allowing the list to be built over several commands. I think the fix is that "save none" purges the list, but the savenone variable is set first. Then it is cleared on the next save command.

    The case for having the program work the same as the shared library, is that there may be non-vector output in both cases, and that may be all the user wants: digital node history, logging from Verilog-A models, script output ...

    Holger, now I have dug a bit, I can take this one. But if you wish to take it, please let me know.

    G

     
  • Holger Vogt

    Holger Vogt - 2026-10-04

    Yes, Giles, please take it.

     
  • Giles Atkinson

    Giles Atkinson - 2026-10-04
    • status: open --> open-accepted
    • assigned_to: Giles Atkinson
     
  • Giles Atkinson

    Giles Atkinson - 2026-10-12
    • status: open-accepted --> open-fixed
     
  • Giles Atkinson

    Giles Atkinson - 2026-10-12

    Fixed: b2f9fefa35

     

    Last edit: Giles Atkinson 2026-10-12

Log in to post a comment.