# Rawfile Output

`outrawfile.h`/`outrawfile.cpp` implement `OutputRawfile`, the only concrete [`Output`](output.md)
writer in the simulator: it produces a Berkeley/ngspice-style `.raw` file (ASCII or binary,
real or complex, padded or unpadded) from a resolved `OutputSource` list. Every analysis core that
writes a raw file (`op`, `dc`, `tran`, `ac`, `noise`, HB, ...) constructs one `OutputRawfile` and
drives it through the `prologue()`/`addPoint()`/`epilogue()` lifecycle defined by `Output`.

## Format flags

```cpp
enum class RawfileFlags {
    None = 0,
    Binary = 1,  // ASCII otherwise
    Complex = 2, // Real otherwise
    Padded = 4,  // Unpadded otherwise
};
```

`OutputRawfile` derives from `FlagBase<RawfileFlags>` (see [Flags](flags.md)) in addition to
`Output`, so `checkFlags()`/`setFlags()`/`clearFlags()` are available on the instance. The
constructor's default is `Flags::Binary|Flags::Padded` (binary, real, padded); analyses that write
complex data (AC-domain results) additionally pass `Flags::Complex`. Every call site chooses
`Binary` vs. `None` (ASCII) based on `SimulatorOptions::rawfileBinary` (see
[Options and Options Management](options.md)) and always includes `Flags::Padded`, so unpadded
output, while supported by the flag, is not currently produced by any analysis.

## Construction and file lifecycle

- **`OutputRawfile(baseName, sources, f)`** appends `.raw` to `baseName` to form the target
  `fileName`, and deletes any pre-existing file at that path immediately - before `prologue()` is
  ever called - so a stale file from a previous run (e.g. a re-run after an aborted analysis)
  cannot be left behind if this run itself is aborted before writing.
- **`~OutputRawfile()`** closes `outStream` unconditionally (a no-op if it is already closed).
- **`prologue(s)`** opens `fileName` in binary+trunc mode (even for ASCII output - only the
  *content* written differs, not the stream's open mode) and writes the header:
  - `Title:` - from `title_` (set via `Output::setTitle()`).
  - `Date:` - a thread-safe formatted timestamp (`formattedTimestamp()`, since `asctime`/
    `localtime` use shared static buffers unsafe to call from multiple threads).
  - `Plotname:` - from `plotname_` (set via `Output::setPlotname()`), e.g. `"Operating Point"`,
    `"Transient Analysis"`.
  - `Flags:` - `complex`/`real` plus an optional trailing ` unpadded`, from `Flags::Complex`/
    `Flags::Padded`.
  - `No. Variables:` - `srcs.size()`.
  - `No. Points:` - written as 16 spaces (a fixed-width placeholder); the stream position right
    after `No. Points: ` is saved in `pointCountPos` so `epilogue()` can seek back and overwrite it
    once the true count is known. Sixteen bytes is generous enough to hold any `size_t` count in
    decimal without growing the file (which would require rewriting everything after it).
  - `Variables:` - one line per source, `\t<index>\t<name>\tnotype`, where `<name>` comes from
    `srcs[i].name()` ([`OutputSource::name()`](output.md)). The type field is always the literal
    string `notype` - the format supports a real type annotation (`voltage`, `current`, ...) that
    this writer does not yet populate.
  - `Binary:` (binary mode) or `Values:` (ASCII mode, which also switches `outStream` to
    `std::scientific`/15-digit precision for everything written after this point).

  `prologue()` resets the `count` of points written so far to 0. If opening the file fails,
  it reports `Status::CreationFailed` and returns `false`; on success it returns `true`.

- **`addPoint(s)`** appends one point (one row) by reading every source in `srcs` via `getR()`
  (real mode) or `getC()` (complex mode) - a direct fan-out of `checkFlags(Flags::Binary)` x
  `checkFlags(Flags::Complex)` into four write loops:
  - Binary+real: raw `double` bytes, no separators.
  - Binary+complex: raw `Complex` (`double,double` pair) bytes, no separators.
  - ASCII+real: `" <count>\t<value>\n"` for the first column, `"\t<value>\n"` for the rest -
    matching the classic ngspice raw-file row layout (point index only on the first column).
  - ASCII+complex: same row layout, each value written as `"<real>,<imag>"`.

  `addPoint()` always increments `count` and always returns `true` - a write failure on the
  underlying `std::ofstream` is not currently detected here (unlike `prologue()`, which does check
  `outStream.fail()`).
- **`epilogue(s)`** seeks back to `pointCountPos` and overwrites the placeholder with the final
  `count`, then seeks to end-of-file, writes one trailing blank line for ASCII output only (binary
  plots are not blank-line-separated, since the point count in the header is enough to know where
  the data ends), and closes the stream. Always returns `true`.
- **`remove(s)`** deletes `fileName` if it exists, and always returns `true`. Used by an analysis
  core to discard an output file outright (e.g. `deleteOutputs()` when output is being
  regenerated under a different name, or was disabled after having been enabled on a previous
  run).

## Design notes

- **Fixed-width point count**: writing `No. Points:` as a placeholder and patching it in
  `epilogue()` avoids buffering the whole file in memory or making two passes over the source
  data - the point count is simply not knowable until every point has been written, and the raw
  format has no trailer field for it.
- **`std::endl` avoided**: the source comments note that `std::endl` is slow because it flushes
  the stream; every line ends with `"\n"` instead, matching the [`Output`](output.md) contract of
  many small, high-frequency `addPoint()` calls.
- **One `OutputRawfile` per output file, not per analysis run**: an analysis core (e.g.
  `OperatingPointCore`, `TransientCore`) owns a single `OutputRawfile*`, created lazily in
  `initializeOutputs()` and destroyed in `finalizeOutputs()`; `prologue()`/`epilogue()` bracket one
  full analysis pass, and `addPoint()` is called once per point produced within that pass. See
  [Abstract Output Support](output.md) for how `srcs` is populated and kept current across
  re-elaboration.
