Skip to content

Conversation

@robtaylor
Copy link

Summary

  • Add support for using ngspice as a shared library (libngspice) instead of external process execution
  • Add CMake configuration options: WITH_NGSPICE_SHARED (default OFF) and WITH_EXTERNAL_SIMULATORS (default ON)
  • Create NgspiceShared wrapper class for the shared library API
  • Support three build configurations:
    1. Both modes enabled (development/testing)
    2. Shared library only (WebAssembly/Emscripten compatible - no QProcess dependency)
    3. External process only (current default behavior)
  • Implement unified SimulatorError enum for type-safe error handling across both backends

Test plan

  • Build with default configuration (external simulators only)
  • Build with WITH_NGSPICE_SHARED=ON WITH_EXTERNAL_SIMULATORS=OFF
  • Build with both options enabled
  • Verify CI builds pass on all platforms
  • Test runtime functionality with shared library backend

🤖 Generated with Claude Code

CLAUDE.md Outdated

### Simulation Flow
1. User creates schematic in GUI
2. Schematic converted to SPICE netlist (via `qucs2spice`)
Copy link
Owner

Choose a reason for hiding this comment

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

This statement is totally wrong. qucs2spice serves only for conversion of some library models that don't provide SPICE netlist. See the qucs2spice.cpp and look for its usage in the code.

CLAUDE.md Outdated
4. Results parsed and visualized in diagrams

### Submodules
- `qucsator_rf/` is a git submodule (currently empty placeholder)
Copy link
Owner

Choose a reason for hiding this comment

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

This is also wrong. It is not an empty placeholder.

CLAUDE.md Outdated
### Debugging Build Issues
- Check component library files in `library/` are installed
- Verify Qt6 development packages present
- Ensure flex/bison/gperf available for parser generation
Copy link
Owner

Choose a reason for hiding this comment

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

Flex/bison is required only for QucsatorRF

CLAUDE.md Outdated
- Review CMake output for missing dependencies

### Working with Schematic Editor
Key interaction code in `mouseactions.cpp` and `schematic.cpp`. Canvas uses custom Qt3-compatibility scroll view (`Q3ScrollView`).
Copy link
Owner

@ra3xdh ra3xdh Oct 9, 2025

Choose a reason for hiding this comment

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

Q3ScrollView view is not is use anymore.

Copy link
Author

Choose a reason for hiding this comment

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

Just noting that currently Schematic still derives from Q3ScrollView. Should I update the CLAUDE.md to note that it is deprecated?

@robtaylor
Copy link
Author

Wow, thank you for the early review! I just treated a draft PR for the CI 😅

You're a great maintainer!

robtaylor added a commit to robtaylor/qucs_s that referenced this pull request Oct 9, 2025
Address maintainer review comments on PR ra3xdh#1497:

1. Correct qucs2spice description - it's for library model format
   conversion, not general schematic-to-netlist conversion
2. Fix qucsator_rf description - it's a real RF simulator submodule,
   not an empty placeholder
3. Move flex/bison from required to optional dependencies (only needed
   for QucsatorRF)
4. Remove Q3ScrollView statement as it's deprecated

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@robtaylor
Copy link
Author

robtaylor commented Oct 9, 2025

Wow, thank you for the early review! I just treated a draft PR for the CI 😅

You're a great maintainer!

Could you approve CI runs for me?

robtaylor added a commit to robtaylor/qucs_s that referenced this pull request Oct 10, 2025
Address maintainer review comments on PR ra3xdh#1497:

1. Correct qucs2spice description - it's for library model format
   conversion, not general schematic-to-netlist conversion
2. Fix qucsator_rf description - it's a real RF simulator submodule,
   not an empty placeholder
3. Move flex/bison from required to optional dependencies (only needed
   for QucsatorRF)
4. Remove Q3ScrollView statement as it's deprecated

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@robtaylor robtaylor force-pushed the feature/ngspice-shared-library branch 2 times, most recently from 672a604 to 9f84c23 Compare October 10, 2025 12:57
Copy link
Author

@robtaylor robtaylor left a comment

Choose a reason for hiding this comment

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

Some notes. Also I feel like there's a clash gonna happen with your approach and AbstractSpiceKernel, but not sure...

#if EXTERNAL_SIMULATORS
return a_simProcess->waitForFinished(10000);
#else
return true; // For shared library mode, blocking isn't needed
Copy link
Author

Choose a reason for hiding this comment

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

Hmm, as the caller fetches results immediately after calling this, we should block for now.
Worth doing another PR to make simulation runs non-blocking.

Comment on lines 60 to 67
#if EXTERNAL_SIMULATORS
ProcessFailedToStart = static_cast<int>(QProcess::FailedToStart), // 0
ProcessCrashed = static_cast<int>(QProcess::Crashed), // 1
ProcessTimedOut = static_cast<int>(QProcess::Timedout), // 2
ProcessWriteError = static_cast<int>(QProcess::WriteError), // 3
ProcessReadError = static_cast<int>(QProcess::ReadError), // 4
ProcessUnknownError = static_cast<int>(QProcess::UnknownError) // 5
#else
Copy link
Author

Choose a reason for hiding this comment

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

Given that we switch on the process codes to output the right SimulatorError, it seems this isn't needed?

Copy link
Author

Choose a reason for hiding this comment

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

If we error out at the top level if WITH_NGSPICE_SHARED is set and NGSPICE_INCLUDE_DIR/NGSPICE_LIBRARY is not found, then these if statement can just use WITH_NGSPICE_SHARED

It might also be neater to collect these all together in one place.

Comment on lines 86 to 92
# Add test for ngspice shared library if enabled
if(WITH_NGSPICE_SHARED AND NGSPICE_LIBRARY)
add_executable(test_ngspice_shared test_ngspice_shared.cpp)
target_link_libraries(test_ngspice_shared PRIVATE extsimkernels Qt6::Core)
add_test(NAME NgspiceSharedTest COMMAND test_ngspice_shared)
message(STATUS "NgspiceShared test enabled")
endif()
Copy link
Author

Choose a reason for hiding this comment

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

I think a top level TESTS_ENABLED flag would be useful here.

Comment on lines +790 to +798
void Ngspice::onSharedOutputReceived(const QString &text)
{
a_output += text;
if (a_console != nullptr) {
a_console->insertPlainText(text);
a_console->moveCursor(QTextCursor::End);
}
}

Copy link
Author

Choose a reason for hiding this comment

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

It appears this is not called from anywhere!

robtaylor and others added 5 commits October 11, 2025 07:05
Document Qucs-S architecture, build system, code organization,
and development workflows to help contributors understand the codebase.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Implements support for running simulations using the ngspice shared library
interface alongside the existing external process (QProcess) execution mode.

Key features:
- CMake options: WITH_NGSPICE_SHARED and WITH_EXTERNAL_SIMULATORS
- NgspiceShared wrapper class for libngspice API integration
- Unified SimulatorError enum for type-safe error handling across backends
- Conditional compilation supporting three build configurations:
  1. Both modes enabled (shared library priority, external fallback)
  2. Shared library only (WebAssembly/Emscripten compatible)
  3. External process only (traditional QProcess-based)
- No breaking changes to existing API

Implementation details:
- Created qucs/extsimkernels/ngspice_shared.{h,cpp} wrapper
- Modified AbstractSpiceKernel to conditionally compile QProcess code
- Updated Ngspice class to dispatch to appropriate backend
- SimulatorError enum uses sequential auto-assignment (NoError=0, then 1,2,3...)
- Background commands (bg_*) rejected in shared library mode
- Thread-safe signal emission using Qt::QueuedConnection
- Updated Xyce simulator for conditional QProcess support

Error handling:
- Fixed ExternSimDialog to use SimulatorError instead of QProcess::ProcessError
- All signal connections updated to use unified error enum
- Comprehensive error messages for both backends
- Test suite validates enum uniqueness and error handling

All three build configurations tested and working on macOS.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Create automated test to verify ngspice shared library integration:
- Tests basic initialization of NgspiceShared class
- Runs simple voltage divider circuit simulation
- Verifies Qt signals/callbacks work correctly
- Tests error handling for invalid commands/netlists
- Validates vector data retrieval from simulation results

Test runs via ctest when WITH_NGSPICE_SHARED=ON is enabled.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Create Qt Test-based UI regression test framework:
- Add tests/test_ngspice_integration.cpp with Qt Test framework
- Tests NgspiceShared initialization, simulation, signals, and data retrieval
- Tests run automatically in CI via ctest

Add CI support for running tests:
- Install xvfb for headless Qt testing on Linux
- Run ctest after build step with verbose output
- Tests validate NgspiceShared integration works correctly

The test suite provides:
- Automated validation of ngspice shared library integration
- Signal/slot mechanism testing
- Simulation result verification
- Foundation for future UI regression tests

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Expanded NgspiceSharedTest to verify behavioral parity between shared
library mode and traditional external process execution. This helps
detect any behavioral differences introduced by the shared library
implementation.

Changes:
- Added NgspiceExternalProcess helper class to run ngspice as external
  process (QProcess-based) for comparison
- Added test_shared_vs_external() that runs the same circuit through
  both execution modes
- Verifies both modes produce expected outputs (analysis completion,
  output vectors, node voltages)
- Uses single global NgspiceShared instance to avoid multiple
  initialization issues (ngspice shared library doesn't handle
  repeated init/cleanup well in same process)
- Added proper cleanup to prevent abort signals during test teardown

Test results:
- Both modes successfully complete circuit simulation
- Both modes produce comparable outputs with expected vectors
- Shared library: Returns structured vector data via API
- External process: Produces text output with analysis results
- All 3 test suites pass (GeometryTest, NgspiceSharedTest, NgspiceIntegration)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@robtaylor robtaylor force-pushed the feature/ngspice-shared-library branch from ac8bf91 to 53147e8 Compare October 11, 2025 06:07
robtaylor and others added 3 commits October 14, 2025 23:23
This commit adds test_simulation_types.cpp which validates different
SPICE simulation types supported by NgspiceShared:

Test cases added:
- DC operating point analysis with voltage divider
- AC frequency sweep analysis with RC filter
- Transient time-domain analysis with RC step response
- Complex RC filter frequency response validation
- RC step response with time constant verification

All tests use raw SPICE netlists and verify:
- Simulation execution completes successfully
- Output vectors are generated correctly
- Expected nodes/data are present in results

Updated CLAUDE.md with comprehensive documentation on:
- New test executable and what it covers
- Guidelines for writing simulation type tests
- Example SPICE netlist structure for tests

All 5 tests pass (including new simulation types test).

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Changes:
- Add libngspice0-dev to Ubuntu dependencies
- Enable WITH_NGSPICE_SHARED=ON in CMake configuration

This allows simulation type tests (DC, AC, Transient) to run in CI
instead of being skipped. The tests validate NgspiceShared integration
with actual circuit simulations.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Critical fixes:
- Fix memory leak in test cleanup by explicitly deleting NgspiceShared
- Remove duplicate test file from qucs/extsimkernels/ directory
- Remove duplicate test build configuration from CMakeLists.txt

Major improvements:
- Add libngspice to macOS CI dependencies (brew install libngspice)
- Enable WITH_NGSPICE_SHARED=ON in macOS CI build configuration
- Clarify background command rejection comments in ngspice_shared.cpp
  (ngspice shared library has built-in threading via cbBGThreadRunning)

These changes address the critical and major issues identified in code
review while maintaining backward compatibility and test coverage.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants