2016-08-11 12:41:07 +00:00
|
|
|
/*
|
|
|
|
* This program source code file is part of KiCad, a free EDA CAD application.
|
|
|
|
*
|
2022-12-05 19:59:10 +00:00
|
|
|
* Copyright (C) 2016-2022 CERN
|
2024-05-08 20:39:23 +00:00
|
|
|
* Copyright (C) 2018-2024 KiCad Developers, see AUTHORS.txt for contributors.
|
2018-06-04 17:48:17 +00:00
|
|
|
*
|
2016-08-11 12:41:07 +00:00
|
|
|
* @author Tomasz Wlostowski <tomasz.wlostowski@cern.ch>
|
|
|
|
* @author Maciej Suminski <maciej.suminski@cern.ch>
|
|
|
|
*
|
|
|
|
* This program is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU General Public License
|
2016-08-11 12:41:43 +00:00
|
|
|
* as published by the Free Software Foundation; either version 3
|
2016-08-11 12:41:07 +00:00
|
|
|
* of the License, or (at your option) any later version.
|
|
|
|
*
|
|
|
|
* This program is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
* GNU General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU General Public License
|
|
|
|
* along with this program; if not, you may find one here:
|
2016-08-11 12:41:43 +00:00
|
|
|
* https://www.gnu.org/licenses/gpl-3.0.html
|
|
|
|
* or you may search the http://www.gnu.org website for the version 3 license,
|
2016-08-11 12:41:07 +00:00
|
|
|
* or you may write to the Free Software Foundation, Inc.,
|
|
|
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
|
|
|
|
*/
|
|
|
|
|
2020-01-22 08:46:16 +00:00
|
|
|
#include <config.h> // Needed for MSW compilation
|
2022-12-11 18:41:01 +00:00
|
|
|
#include <common.h>
|
|
|
|
#include <locale_io.h>
|
|
|
|
#include <fmt/core.h>
|
|
|
|
#include <paths.h>
|
2020-01-21 18:52:59 +00:00
|
|
|
|
2023-07-14 09:21:58 +00:00
|
|
|
#include "spice_circuit_model.h"
|
2016-08-11 12:41:07 +00:00
|
|
|
#include "ngspice.h"
|
2023-07-14 09:21:58 +00:00
|
|
|
#include "simulator_reporter.h"
|
2021-03-19 13:04:28 +00:00
|
|
|
#include "spice_settings.h"
|
2021-03-18 19:31:02 +00:00
|
|
|
|
2016-10-13 11:25:06 +00:00
|
|
|
#include <wx/stdpaths.h>
|
|
|
|
#include <wx/dir.h>
|
2022-12-11 18:41:01 +00:00
|
|
|
#include <wx/log.h>
|
2020-01-21 18:52:59 +00:00
|
|
|
|
2018-03-21 09:42:39 +00:00
|
|
|
#include <stdexcept>
|
2020-05-08 14:34:28 +00:00
|
|
|
#include <algorithm>
|
|
|
|
|
2021-03-18 19:31:02 +00:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Flag to enable debug output of Ngspice simulator.
|
|
|
|
*
|
|
|
|
* Use "KICAD_NGSPICE" to enable Ngspice simulator tracing.
|
|
|
|
*
|
|
|
|
* @ingroup trace_env_vars
|
|
|
|
*/
|
2018-03-23 10:14:33 +00:00
|
|
|
static const wxChar* const traceNgspice = wxT( "KICAD_NGSPICE" );
|
|
|
|
|
2021-03-18 19:31:02 +00:00
|
|
|
|
|
|
|
NGSPICE::NGSPICE() :
|
|
|
|
m_ngSpice_Init( nullptr ),
|
|
|
|
m_ngSpice_Circ( nullptr ),
|
|
|
|
m_ngSpice_Command( nullptr ),
|
|
|
|
m_ngGet_Vec_Info( nullptr ),
|
2024-05-08 20:39:23 +00:00
|
|
|
m_ngCM_Input_Path( nullptr ),
|
2021-03-18 19:31:02 +00:00
|
|
|
m_ngSpice_CurPlot( nullptr ),
|
|
|
|
m_ngSpice_AllPlots( nullptr ),
|
|
|
|
m_ngSpice_AllVecs( nullptr ),
|
|
|
|
m_ngSpice_Running( nullptr ),
|
2023-12-28 19:31:03 +00:00
|
|
|
m_ngSpice_LockRealloc( nullptr ),
|
|
|
|
m_ngSpice_UnlockRealloc( nullptr ),
|
2021-03-18 19:31:02 +00:00
|
|
|
m_error( false )
|
2016-08-11 12:41:01 +00:00
|
|
|
{
|
2018-03-21 09:42:39 +00:00
|
|
|
init_dll();
|
2016-08-11 12:41:07 +00:00
|
|
|
}
|
2016-08-11 12:41:01 +00:00
|
|
|
|
|
|
|
|
2022-08-15 17:47:23 +00:00
|
|
|
NGSPICE::~NGSPICE() = default;
|
2016-08-11 12:41:01 +00:00
|
|
|
|
2016-08-11 12:41:07 +00:00
|
|
|
|
2024-02-13 14:38:02 +00:00
|
|
|
void NGSPICE::updateNgspiceSettings()
|
2016-08-11 12:41:01 +00:00
|
|
|
{
|
2021-03-18 19:31:02 +00:00
|
|
|
for( const std::string& command : GetSettingCommands() )
|
|
|
|
{
|
|
|
|
wxLogTrace( traceNgspice, "Sending Ngspice configuration command '%s'.", command );
|
|
|
|
Command( command );
|
|
|
|
}
|
2016-08-11 12:41:01 +00:00
|
|
|
}
|
|
|
|
|
2016-08-11 12:41:07 +00:00
|
|
|
|
2024-02-13 14:38:02 +00:00
|
|
|
void NGSPICE::Init( const SPICE_SETTINGS* aSettings )
|
|
|
|
{
|
|
|
|
Command( "reset" );
|
|
|
|
updateNgspiceSettings();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-07-05 10:53:19 +00:00
|
|
|
wxString NGSPICE::CurrentPlotName() const
|
|
|
|
{
|
|
|
|
return wxString( m_ngSpice_CurPlot() );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-07-04 10:04:20 +00:00
|
|
|
std::vector<std::string> NGSPICE::AllVectors() const
|
2020-04-15 01:51:58 +00:00
|
|
|
{
|
|
|
|
LOCALE_IO c_locale; // ngspice works correctly only with C locale
|
|
|
|
char* currentPlot = m_ngSpice_CurPlot();
|
2023-07-04 10:04:20 +00:00
|
|
|
char** allVectors = m_ngSpice_AllVecs( currentPlot );
|
|
|
|
int noOfVectors = 0;
|
2020-04-15 01:51:58 +00:00
|
|
|
|
2022-12-11 18:41:01 +00:00
|
|
|
std::vector<std::string> retVal;
|
2021-03-18 19:31:02 +00:00
|
|
|
|
2023-07-04 10:04:20 +00:00
|
|
|
if( allVectors != nullptr )
|
2021-02-16 23:37:57 +00:00
|
|
|
{
|
2023-07-04 10:04:20 +00:00
|
|
|
for( char** plot = allVectors; *plot != nullptr; plot++ )
|
|
|
|
noOfVectors++;
|
2020-04-15 01:51:58 +00:00
|
|
|
|
2023-07-04 10:04:20 +00:00
|
|
|
retVal.reserve( noOfVectors );
|
2021-03-08 18:49:46 +00:00
|
|
|
|
2023-07-04 10:04:20 +00:00
|
|
|
for( int i = 0; i < noOfVectors; i++, allVectors++ )
|
2021-02-16 23:37:57 +00:00
|
|
|
{
|
2023-07-04 10:04:20 +00:00
|
|
|
std::string vec = *allVectors;
|
2021-03-08 18:49:46 +00:00
|
|
|
retVal.push_back( vec );
|
2021-02-16 23:37:57 +00:00
|
|
|
}
|
2020-04-15 01:51:58 +00:00
|
|
|
}
|
|
|
|
|
2021-02-16 23:37:57 +00:00
|
|
|
|
2020-04-15 01:51:58 +00:00
|
|
|
return retVal;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-07-05 10:53:19 +00:00
|
|
|
std::vector<COMPLEX> NGSPICE::GetComplexVector( const std::string& aName, int aMaxLen )
|
2016-08-11 12:41:31 +00:00
|
|
|
{
|
2022-12-11 18:41:01 +00:00
|
|
|
LOCALE_IO c_locale; // ngspice works correctly only with C locale
|
|
|
|
std::vector<COMPLEX> data;
|
2023-12-28 19:31:03 +00:00
|
|
|
NGSPICE_LOCK_REALLOC lock( this );
|
2016-08-11 12:41:31 +00:00
|
|
|
|
2023-10-03 11:54:34 +00:00
|
|
|
if( aMaxLen == 0 )
|
|
|
|
return data;
|
|
|
|
|
|
|
|
if( vector_info* vi = m_ngGet_Vec_Info( (char*) aName.c_str() ) )
|
2016-08-11 12:41:31 +00:00
|
|
|
{
|
|
|
|
int length = aMaxLen < 0 ? vi->v_length : std::min( aMaxLen, vi->v_length );
|
|
|
|
data.reserve( length );
|
|
|
|
|
|
|
|
if( vi->v_realdata )
|
|
|
|
{
|
|
|
|
for( int i = 0; i < length; i++ )
|
2019-12-05 15:41:21 +00:00
|
|
|
data.emplace_back( vi->v_realdata[i], 0.0 );
|
2016-08-11 12:41:31 +00:00
|
|
|
}
|
|
|
|
else if( vi->v_compdata )
|
|
|
|
{
|
|
|
|
for( int i = 0; i < length; i++ )
|
2019-12-05 15:41:21 +00:00
|
|
|
data.emplace_back( vi->v_compdata[i].cx_real, vi->v_compdata[i].cx_imag );
|
2016-08-11 12:41:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-07-05 10:53:19 +00:00
|
|
|
std::vector<double> NGSPICE::GetRealVector( const std::string& aName, int aMaxLen )
|
2016-08-11 12:41:01 +00:00
|
|
|
{
|
2023-12-28 19:31:03 +00:00
|
|
|
LOCALE_IO c_locale; // ngspice works correctly only with C locale
|
|
|
|
std::vector<double> data;
|
|
|
|
NGSPICE_LOCK_REALLOC lock( this );
|
2016-08-11 12:41:01 +00:00
|
|
|
|
2023-10-03 11:54:34 +00:00
|
|
|
if( aMaxLen == 0 )
|
|
|
|
return data;
|
|
|
|
|
|
|
|
if( vector_info* vi = m_ngGet_Vec_Info( (char*) aName.c_str() ) )
|
2016-08-11 12:41:31 +00:00
|
|
|
{
|
|
|
|
int length = aMaxLen < 0 ? vi->v_length : std::min( aMaxLen, vi->v_length );
|
|
|
|
data.reserve( length );
|
|
|
|
|
|
|
|
if( vi->v_realdata )
|
|
|
|
{
|
|
|
|
for( int i = 0; i < length; i++ )
|
|
|
|
data.push_back( vi->v_realdata[i] );
|
|
|
|
}
|
|
|
|
else if( vi->v_compdata )
|
|
|
|
{
|
|
|
|
for( int i = 0; i < length; i++ )
|
|
|
|
{
|
2021-04-14 19:01:39 +00:00
|
|
|
wxASSERT( vi->v_compdata[i].cx_imag == 0.0 );
|
2016-08-11 12:41:31 +00:00
|
|
|
data.push_back( vi->v_compdata[i].cx_real );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-07-05 10:53:19 +00:00
|
|
|
std::vector<double> NGSPICE::GetImaginaryVector( const std::string& aName, int aMaxLen )
|
2016-08-11 12:41:31 +00:00
|
|
|
{
|
2023-12-28 19:31:03 +00:00
|
|
|
LOCALE_IO c_locale; // ngspice works correctly only with C locale
|
|
|
|
std::vector<double> data;
|
|
|
|
NGSPICE_LOCK_REALLOC lock( this );
|
2016-08-11 12:41:01 +00:00
|
|
|
|
2023-10-03 11:54:34 +00:00
|
|
|
if( aMaxLen == 0 )
|
|
|
|
return data;
|
|
|
|
|
|
|
|
if( vector_info* vi = m_ngGet_Vec_Info( (char*) aName.c_str() ) )
|
2016-08-11 12:41:07 +00:00
|
|
|
{
|
2016-08-11 12:41:31 +00:00
|
|
|
int length = aMaxLen < 0 ? vi->v_length : std::min( aMaxLen, vi->v_length );
|
|
|
|
data.reserve( length );
|
|
|
|
|
|
|
|
if( vi->v_compdata )
|
|
|
|
{
|
|
|
|
for( int i = 0; i < length; i++ )
|
|
|
|
data.push_back( vi->v_compdata[i].cx_imag );
|
|
|
|
}
|
|
|
|
}
|
2016-08-11 12:41:11 +00:00
|
|
|
|
2016-08-11 12:41:31 +00:00
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-07-05 10:53:19 +00:00
|
|
|
std::vector<double> NGSPICE::GetGainVector( const std::string& aName, int aMaxLen )
|
2016-08-11 12:41:31 +00:00
|
|
|
{
|
2023-12-28 19:31:03 +00:00
|
|
|
LOCALE_IO c_locale; // ngspice works correctly only with C locale
|
|
|
|
std::vector<double> data;
|
|
|
|
NGSPICE_LOCK_REALLOC lock( this );
|
2016-08-11 12:41:31 +00:00
|
|
|
|
2023-10-03 11:54:34 +00:00
|
|
|
if( aMaxLen == 0 )
|
|
|
|
return data;
|
|
|
|
|
|
|
|
if( vector_info* vi = m_ngGet_Vec_Info( (char*) aName.c_str() ) )
|
2016-08-11 12:41:31 +00:00
|
|
|
{
|
|
|
|
int length = aMaxLen < 0 ? vi->v_length : std::min( aMaxLen, vi->v_length );
|
|
|
|
data.reserve( length );
|
|
|
|
|
|
|
|
if( vi->v_realdata )
|
|
|
|
{
|
|
|
|
for( int i = 0; i < length; i++ )
|
|
|
|
data.push_back( vi->v_realdata[i] );
|
|
|
|
}
|
|
|
|
else if( vi->v_compdata )
|
|
|
|
{
|
|
|
|
for( int i = 0; i < length; i++ )
|
|
|
|
data.push_back( hypot( vi->v_compdata[i].cx_real, vi->v_compdata[i].cx_imag ) );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-07-05 10:53:19 +00:00
|
|
|
std::vector<double> NGSPICE::GetPhaseVector( const std::string& aName, int aMaxLen )
|
2016-08-11 12:41:31 +00:00
|
|
|
{
|
2023-12-28 19:31:03 +00:00
|
|
|
LOCALE_IO c_locale; // ngspice works correctly only with C locale
|
|
|
|
std::vector<double> data;
|
|
|
|
NGSPICE_LOCK_REALLOC lock( this );
|
2016-08-11 12:41:31 +00:00
|
|
|
|
2023-10-03 11:54:34 +00:00
|
|
|
if( aMaxLen == 0 )
|
|
|
|
return data;
|
|
|
|
|
|
|
|
if( vector_info* vi = m_ngGet_Vec_Info( (char*) aName.c_str() ) )
|
2016-08-11 12:41:31 +00:00
|
|
|
{
|
|
|
|
int length = aMaxLen < 0 ? vi->v_length : std::min( aMaxLen, vi->v_length );
|
|
|
|
data.reserve( length );
|
|
|
|
|
|
|
|
if( vi->v_realdata )
|
|
|
|
{
|
|
|
|
for( int i = 0; i < length; i++ )
|
|
|
|
data.push_back( 0.0 ); // well, that's life
|
|
|
|
}
|
|
|
|
else if( vi->v_compdata )
|
|
|
|
{
|
|
|
|
for( int i = 0; i < length; i++ )
|
|
|
|
data.push_back( atan2( vi->v_compdata[i].cx_imag, vi->v_compdata[i].cx_real ) );
|
|
|
|
}
|
2016-08-11 12:41:07 +00:00
|
|
|
}
|
2016-08-11 12:41:01 +00:00
|
|
|
|
|
|
|
return data;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-07-05 17:25:52 +00:00
|
|
|
bool NGSPICE::Attach( const std::shared_ptr<SIMULATION_MODEL>& aModel, const wxString& aSimCommand,
|
2024-05-08 20:39:23 +00:00
|
|
|
unsigned aSimOptions, const wxString& aInputPath, REPORTER& aReporter )
|
2022-03-05 23:49:25 +00:00
|
|
|
{
|
2023-07-14 09:21:58 +00:00
|
|
|
SPICE_CIRCUIT_MODEL* model = dynamic_cast<SPICE_CIRCUIT_MODEL*>( aModel.get() );
|
|
|
|
STRING_FORMATTER formatter;
|
2022-03-05 23:49:25 +00:00
|
|
|
|
2024-05-08 20:39:23 +00:00
|
|
|
setCodemodelsInputPath( aInputPath.ToStdString() );
|
|
|
|
|
2023-07-05 17:25:52 +00:00
|
|
|
if( model && model->GetNetlist( aSimCommand, aSimOptions, &formatter, aReporter ) )
|
2022-03-05 23:49:25 +00:00
|
|
|
{
|
2024-05-08 20:39:23 +00:00
|
|
|
SIMULATOR::Attach( aModel, aSimCommand, aSimOptions, aInputPath, aReporter );
|
2024-02-13 14:38:02 +00:00
|
|
|
updateNgspiceSettings();
|
2022-03-05 23:49:25 +00:00
|
|
|
LoadNetlist( formatter.GetString() );
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2024-05-08 20:39:23 +00:00
|
|
|
SIMULATOR::Attach( nullptr, wxEmptyString, 0, wxEmptyString, aReporter );
|
2022-03-05 23:49:25 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-12-11 18:41:01 +00:00
|
|
|
bool NGSPICE::LoadNetlist( const std::string& aNetlist )
|
2016-08-11 12:41:01 +00:00
|
|
|
{
|
2022-12-11 18:41:01 +00:00
|
|
|
LOCALE_IO c_locale; // ngspice works correctly only with C locale
|
|
|
|
std::vector<char*> lines;
|
|
|
|
std::stringstream ss( aNetlist );
|
2016-08-11 12:41:01 +00:00
|
|
|
|
2018-03-03 16:36:26 +00:00
|
|
|
m_netlist = "";
|
|
|
|
|
2016-08-11 12:42:17 +00:00
|
|
|
while( !ss.eof() )
|
2016-08-11 12:41:01 +00:00
|
|
|
{
|
|
|
|
char line[1024];
|
2016-08-11 12:42:17 +00:00
|
|
|
ss.getline( line, sizeof( line ) );
|
|
|
|
lines.push_back( strdup( line ) );
|
2018-03-03 16:36:26 +00:00
|
|
|
m_netlist += std::string( line ) + std::string( "\n" );
|
2016-08-11 12:41:01 +00:00
|
|
|
}
|
2016-08-11 12:42:17 +00:00
|
|
|
|
2018-03-22 10:58:55 +00:00
|
|
|
lines.push_back( nullptr ); // sentinel, as requested in ngSpice_Circ description
|
2022-03-05 23:49:25 +00:00
|
|
|
|
|
|
|
Command( "remcirc" );
|
2022-08-10 15:53:13 +00:00
|
|
|
bool success = !m_ngSpice_Circ( lines.data() );
|
2016-08-11 12:41:01 +00:00
|
|
|
|
2022-12-05 19:59:10 +00:00
|
|
|
for( char* line : lines )
|
2017-03-01 12:56:24 +00:00
|
|
|
free( line );
|
2016-08-11 12:41:01 +00:00
|
|
|
|
2022-08-10 15:53:13 +00:00
|
|
|
return success;
|
2016-08-11 12:41:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-08-11 12:41:07 +00:00
|
|
|
bool NGSPICE::Run()
|
|
|
|
{
|
2023-06-17 22:53:57 +00:00
|
|
|
LOCALE_IO toggle; // ngspice works correctly only with C locale
|
|
|
|
return Command( "bg_run" ); // bg_* commands execute in a separate thread
|
2016-08-11 12:41:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool NGSPICE::Stop()
|
|
|
|
{
|
2023-06-17 22:53:57 +00:00
|
|
|
LOCALE_IO c_locale; // ngspice works correctly only with C locale
|
2016-08-15 13:52:07 +00:00
|
|
|
return Command( "bg_halt" ); // bg_* commands execute in a separate thread
|
2016-08-11 12:41:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
bool NGSPICE::IsRunning()
|
|
|
|
{
|
2021-05-05 18:26:56 +00:00
|
|
|
// No need to use C locale here
|
2018-03-21 09:42:39 +00:00
|
|
|
return m_ngSpice_Running();
|
2016-08-11 12:41:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-12-11 18:41:01 +00:00
|
|
|
bool NGSPICE::Command( const std::string& aCmd )
|
2016-08-11 12:41:01 +00:00
|
|
|
{
|
2016-08-15 13:52:07 +00:00
|
|
|
LOCALE_IO c_locale; // ngspice works correctly only with C locale
|
2018-03-21 16:22:56 +00:00
|
|
|
validate();
|
2022-08-10 15:53:13 +00:00
|
|
|
return !m_ngSpice_Command( (char*) aCmd.c_str() );
|
2016-08-11 12:41:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2023-02-25 22:42:52 +00:00
|
|
|
wxString NGSPICE::GetXAxis( SIM_TYPE aType ) const
|
2016-08-11 12:41:51 +00:00
|
|
|
{
|
|
|
|
switch( aType )
|
|
|
|
{
|
2021-03-18 19:31:02 +00:00
|
|
|
case ST_AC:
|
2023-07-05 17:25:52 +00:00
|
|
|
case ST_SP:
|
2023-07-03 22:11:43 +00:00
|
|
|
case ST_NOISE:
|
2023-07-05 17:25:52 +00:00
|
|
|
case ST_FFT:
|
|
|
|
return wxS( "frequency" );
|
2023-07-03 22:11:43 +00:00
|
|
|
|
2021-03-18 19:31:02 +00:00
|
|
|
case ST_DC:
|
|
|
|
// find plot, which ends with "-sweep"
|
2023-07-04 10:04:20 +00:00
|
|
|
for( wxString vector : AllVectors() )
|
2021-03-18 19:31:02 +00:00
|
|
|
{
|
2023-07-04 10:04:20 +00:00
|
|
|
if( vector.Lower().EndsWith( wxS( "-sweep" ) ) )
|
|
|
|
return vector;
|
2021-03-18 19:31:02 +00:00
|
|
|
}
|
2023-02-25 22:42:52 +00:00
|
|
|
|
|
|
|
return wxS( "sweep" );
|
2016-08-11 12:41:51 +00:00
|
|
|
|
2023-07-05 17:25:52 +00:00
|
|
|
case ST_TRAN:
|
2023-02-25 22:42:52 +00:00
|
|
|
return wxS( "time" );
|
2016-08-11 12:41:51 +00:00
|
|
|
|
2021-03-18 19:31:02 +00:00
|
|
|
default:
|
2023-02-25 22:42:52 +00:00
|
|
|
return wxEmptyString;
|
2016-08-11 12:41:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-11 12:42:17 +00:00
|
|
|
|
2021-03-18 19:31:02 +00:00
|
|
|
std::vector<std::string> NGSPICE::GetSettingCommands() const
|
|
|
|
{
|
2023-07-14 09:21:58 +00:00
|
|
|
const NGSPICE_SETTINGS* settings = dynamic_cast<const NGSPICE_SETTINGS*>( Settings().get() );
|
2021-03-18 19:31:02 +00:00
|
|
|
|
|
|
|
std::vector<std::string> commands;
|
|
|
|
|
|
|
|
wxCHECK( settings, commands );
|
|
|
|
|
2023-07-14 09:21:58 +00:00
|
|
|
switch( settings->GetCompatibilityMode() )
|
2021-03-18 19:31:02 +00:00
|
|
|
{
|
2023-07-14 09:21:58 +00:00
|
|
|
case NGSPICE_COMPATIBILITY_MODE::USER_CONFIG: break;
|
|
|
|
case NGSPICE_COMPATIBILITY_MODE::NGSPICE: commands.emplace_back( "unset ngbehavior" ); break;
|
|
|
|
case NGSPICE_COMPATIBILITY_MODE::PSPICE: commands.emplace_back( "set ngbehavior=psa" ); break;
|
|
|
|
case NGSPICE_COMPATIBILITY_MODE::LTSPICE: commands.emplace_back( "set ngbehavior=lta" ); break;
|
|
|
|
case NGSPICE_COMPATIBILITY_MODE::LT_PSPICE: commands.emplace_back( "set ngbehavior=ltpsa" ); break;
|
|
|
|
case NGSPICE_COMPATIBILITY_MODE::HSPICE: commands.emplace_back( "set ngbehavior=hsa" ); break;
|
|
|
|
default: wxFAIL_MSG( wxString::Format( "Undefined NGSPICE_COMPATIBILITY_MODE %d.",
|
|
|
|
settings->GetCompatibilityMode() ) ); break;
|
2021-03-18 19:31:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return commands;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const std::string NGSPICE::GetNetlist() const
|
|
|
|
{
|
|
|
|
return m_netlist;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2018-03-21 09:42:39 +00:00
|
|
|
void NGSPICE::init_dll()
|
2016-08-11 12:41:01 +00:00
|
|
|
{
|
2016-08-18 16:10:00 +00:00
|
|
|
if( m_initialized )
|
|
|
|
return;
|
2016-08-11 12:42:18 +00:00
|
|
|
|
2016-08-15 13:52:07 +00:00
|
|
|
LOCALE_IO c_locale; // ngspice works correctly only with C locale
|
2018-03-23 10:14:33 +00:00
|
|
|
const wxStandardPaths& stdPaths = wxStandardPaths::Get();
|
2018-03-21 09:42:39 +00:00
|
|
|
|
2018-03-23 10:12:21 +00:00
|
|
|
if( m_dll.IsLoaded() ) // enable force reload
|
|
|
|
m_dll.Unload();
|
|
|
|
|
2021-03-18 19:31:02 +00:00
|
|
|
// Extra effort to find libngspice
|
|
|
|
// @todo Shouldn't we be using the normal KiCad path searching mechanism here?
|
2018-10-29 13:23:03 +00:00
|
|
|
wxFileName dllFile( "", NGSPICE_DLL_FILE );
|
2019-04-14 17:59:27 +00:00
|
|
|
#if defined(__WINDOWS__)
|
2021-03-18 19:31:02 +00:00
|
|
|
#if defined( _MSC_VER )
|
2022-12-11 19:19:56 +00:00
|
|
|
const std::vector<std::string> dllPaths = { "" };
|
2021-03-18 19:31:02 +00:00
|
|
|
#else
|
2022-12-11 19:19:56 +00:00
|
|
|
const std::vector<std::string> dllPaths = { "", "/mingw64/bin", "/mingw32/bin" };
|
2021-03-18 19:31:02 +00:00
|
|
|
#endif
|
2022-01-31 23:57:56 +00:00
|
|
|
#elif defined(__WXMAC__)
|
2022-12-11 18:41:01 +00:00
|
|
|
const std::vector<std::string> dllPaths = {
|
2021-02-17 04:26:46 +00:00
|
|
|
PATHS::GetOSXKicadUserDataDir().ToStdString() + "/PlugIns/ngspice",
|
|
|
|
PATHS::GetOSXKicadMachineDataDir().ToStdString() + "/PlugIns/ngspice",
|
2021-03-18 19:31:02 +00:00
|
|
|
|
2018-03-23 10:14:33 +00:00
|
|
|
// when running kicad.app
|
2020-07-08 18:29:16 +00:00
|
|
|
stdPaths.GetPluginsDir().ToStdString() + "/sim",
|
2021-03-18 19:31:02 +00:00
|
|
|
|
2018-03-23 10:14:33 +00:00
|
|
|
// when running eeschema.app
|
2021-03-18 19:31:02 +00:00
|
|
|
wxFileName( stdPaths.GetExecutablePath() ).GetPath().ToStdString() +
|
|
|
|
"/../../../../../Contents/PlugIns/sim"
|
2018-03-23 10:14:33 +00:00
|
|
|
};
|
2019-04-14 17:59:27 +00:00
|
|
|
#else // Unix systems
|
2022-12-11 19:43:10 +00:00
|
|
|
const std::vector<std::string> dllPaths = { "/usr/local/lib" };
|
2019-04-14 17:59:27 +00:00
|
|
|
#endif
|
2018-03-23 10:14:33 +00:00
|
|
|
|
2019-04-14 17:59:27 +00:00
|
|
|
#if defined(__WINDOWS__) || (__WXMAC__)
|
2018-03-23 10:14:33 +00:00
|
|
|
for( const auto& path : dllPaths )
|
|
|
|
{
|
|
|
|
dllFile.SetPath( path );
|
|
|
|
wxLogTrace( traceNgspice, "libngspice search path: %s", dllFile.GetFullPath() );
|
|
|
|
m_dll.Load( dllFile.GetFullPath(), wxDL_VERBATIM | wxDL_QUIET | wxDL_NOW );
|
|
|
|
|
|
|
|
if( m_dll.IsLoaded() )
|
|
|
|
{
|
|
|
|
wxLogTrace( traceNgspice, "libngspice path found in: %s", dllFile.GetFullPath() );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
2018-04-19 12:48:31 +00:00
|
|
|
|
|
|
|
if( !m_dll.IsLoaded() ) // try also the system libraries
|
|
|
|
m_dll.Load( wxDynamicLibrary::CanonicalizeName( "ngspice" ) );
|
2021-03-18 19:31:02 +00:00
|
|
|
#else
|
2019-04-14 17:59:27 +00:00
|
|
|
// First, try the system libraries
|
|
|
|
m_dll.Load( NGSPICE_DLL_FILE, wxDL_VERBATIM | wxDL_QUIET | wxDL_NOW );
|
|
|
|
|
|
|
|
// If failed, try some other paths:
|
|
|
|
if( !m_dll.IsLoaded() )
|
|
|
|
{
|
|
|
|
for( const auto& path : dllPaths )
|
|
|
|
{
|
|
|
|
dllFile.SetPath( path );
|
|
|
|
wxLogTrace( traceNgspice, "libngspice search path: %s", dllFile.GetFullPath() );
|
|
|
|
m_dll.Load( dllFile.GetFullPath(), wxDL_VERBATIM | wxDL_QUIET | wxDL_NOW );
|
|
|
|
|
|
|
|
if( m_dll.IsLoaded() )
|
|
|
|
{
|
|
|
|
wxLogTrace( traceNgspice, "libngspice path found in: %s", dllFile.GetFullPath() );
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
2018-03-21 09:42:39 +00:00
|
|
|
|
2018-03-23 10:12:21 +00:00
|
|
|
if( !m_dll.IsLoaded() )
|
2018-03-21 09:42:39 +00:00
|
|
|
throw std::runtime_error( "Missing ngspice shared library" );
|
|
|
|
|
|
|
|
m_error = false;
|
|
|
|
|
|
|
|
// Obtain function pointers
|
2018-03-23 10:12:21 +00:00
|
|
|
m_ngSpice_Init = (ngSpice_Init) m_dll.GetSymbol( "ngSpice_Init" );
|
|
|
|
m_ngSpice_Circ = (ngSpice_Circ) m_dll.GetSymbol( "ngSpice_Circ" );
|
|
|
|
m_ngSpice_Command = (ngSpice_Command) m_dll.GetSymbol( "ngSpice_Command" );
|
|
|
|
m_ngGet_Vec_Info = (ngGet_Vec_Info) m_dll.GetSymbol( "ngGet_Vec_Info" );
|
2024-05-08 20:39:23 +00:00
|
|
|
m_ngCM_Input_Path = (ngCM_Input_Path) m_dll.GetSymbol( "ngCM_Input_Path" );
|
2020-04-15 01:51:58 +00:00
|
|
|
m_ngSpice_CurPlot = (ngSpice_CurPlot) m_dll.GetSymbol( "ngSpice_CurPlot" );
|
2018-03-23 10:12:21 +00:00
|
|
|
m_ngSpice_AllPlots = (ngSpice_AllPlots) m_dll.GetSymbol( "ngSpice_AllPlots" );
|
|
|
|
m_ngSpice_AllVecs = (ngSpice_AllVecs) m_dll.GetSymbol( "ngSpice_AllVecs" );
|
|
|
|
m_ngSpice_Running = (ngSpice_Running) m_dll.GetSymbol( "ngSpice_running" ); // it is not a typo
|
2018-03-21 09:42:39 +00:00
|
|
|
|
2023-12-28 21:52:26 +00:00
|
|
|
if( m_dll.HasSymbol( "ngSpice_LockRealloc" ) )
|
2023-12-28 19:31:03 +00:00
|
|
|
{
|
|
|
|
m_ngSpice_LockRealloc = (ngSpice_LockRealloc) m_dll.GetSymbol( "ngSpice_LockRealloc" );
|
|
|
|
m_ngSpice_UnlockRealloc = (ngSpice_UnlockRealloc) m_dll.GetSymbol( "ngSpice_UnlockRealloc" );
|
|
|
|
}
|
|
|
|
|
2022-12-11 18:41:01 +00:00
|
|
|
m_ngSpice_Init( &cbSendChar, &cbSendStat, &cbControlledExit, nullptr, nullptr,
|
2021-03-18 19:31:02 +00:00
|
|
|
&cbBGThreadRunning, this );
|
2016-08-15 11:52:13 +00:00
|
|
|
|
2016-10-13 11:25:06 +00:00
|
|
|
// Load a custom spinit file, to fix the problem with loading .cm files
|
|
|
|
// Switch to the executable directory, so the relative paths are correct
|
|
|
|
wxString cwd( wxGetCwd() );
|
2018-03-23 10:14:33 +00:00
|
|
|
wxFileName exeDir( stdPaths.GetExecutablePath() );
|
2016-10-13 11:25:06 +00:00
|
|
|
wxSetWorkingDirectory( exeDir.GetPath() );
|
|
|
|
|
|
|
|
// Find *.cm files
|
2022-12-11 18:41:01 +00:00
|
|
|
std::string cmPath = findCmPath();
|
2016-10-13 11:25:06 +00:00
|
|
|
|
|
|
|
// __CMPATH is used in custom spinit file to point to the codemodels directory
|
|
|
|
if( !cmPath.empty() )
|
|
|
|
Command( "set __CMPATH=\"" + cmPath + "\"" );
|
|
|
|
|
|
|
|
// Possible relative locations for spinit file
|
2022-12-11 18:41:01 +00:00
|
|
|
const std::vector<std::string> spiceinitPaths =
|
2016-10-13 11:25:06 +00:00
|
|
|
{
|
|
|
|
".",
|
2018-03-23 10:14:33 +00:00
|
|
|
#ifdef __WXMAC__
|
2020-07-08 18:29:16 +00:00
|
|
|
stdPaths.GetPluginsDir().ToStdString() + "/sim/ngspice/scripts",
|
2021-03-18 19:31:02 +00:00
|
|
|
wxFileName( stdPaths.GetExecutablePath() ).GetPath().ToStdString() +
|
|
|
|
"/../../../../../Contents/PlugIns/sim/ngspice/scripts"
|
|
|
|
#endif
|
2016-10-13 11:25:06 +00:00
|
|
|
"../share/kicad",
|
|
|
|
"../share",
|
|
|
|
"../../share/kicad",
|
|
|
|
"../../share"
|
|
|
|
};
|
|
|
|
|
|
|
|
bool foundSpiceinit = false;
|
|
|
|
|
|
|
|
for( const auto& path : spiceinitPaths )
|
|
|
|
{
|
2018-03-23 10:14:33 +00:00
|
|
|
wxLogTrace( traceNgspice, "ngspice init script search path: %s", path );
|
|
|
|
|
2016-10-13 11:25:06 +00:00
|
|
|
if( loadSpinit( path + "/spiceinit" ) )
|
|
|
|
{
|
2018-03-23 10:14:33 +00:00
|
|
|
wxLogTrace( traceNgspice, "ngspice path found in: %s", path );
|
2016-10-13 11:25:06 +00:00
|
|
|
foundSpiceinit = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Last chance to load codemodel files, we have not found
|
|
|
|
// spiceinit file, but we know the path to *.cm files
|
|
|
|
if( !foundSpiceinit && !cmPath.empty() )
|
|
|
|
loadCodemodels( cmPath );
|
|
|
|
|
|
|
|
// Restore the working directory
|
|
|
|
wxSetWorkingDirectory( cwd );
|
|
|
|
|
2018-03-21 09:42:39 +00:00
|
|
|
// Workarounds to avoid hang ups on certain errors
|
|
|
|
// These commands have to be called, no matter what is in the spinit file
|
2023-02-21 09:06:02 +00:00
|
|
|
// We have to allow interactive for user-defined signals. Hopefully whatever bug this was
|
|
|
|
// meant to address has gone away in the last 5 years...
|
|
|
|
//Command( "unset interactive" );
|
2016-10-13 11:25:06 +00:00
|
|
|
Command( "set noaskquit" );
|
|
|
|
Command( "set nomoremode" );
|
2016-08-18 16:10:00 +00:00
|
|
|
|
2022-10-18 03:17:21 +00:00
|
|
|
// reset and remcirc give an error if no circuit is loaded, so load an empty circuit at the
|
|
|
|
// start.
|
|
|
|
|
2022-12-11 18:41:01 +00:00
|
|
|
std::vector<char*> lines;
|
2022-10-18 03:17:21 +00:00
|
|
|
lines.push_back( strdup( "*" ) );
|
|
|
|
lines.push_back( strdup( ".end" ) );
|
|
|
|
lines.push_back( nullptr ); // Sentinel.
|
|
|
|
|
|
|
|
m_ngSpice_Circ( lines.data() );
|
|
|
|
|
|
|
|
for( auto line : lines )
|
|
|
|
free( line );
|
|
|
|
|
2016-08-18 16:10:00 +00:00
|
|
|
m_initialized = true;
|
2016-08-11 12:41:01 +00:00
|
|
|
}
|
|
|
|
|
2016-08-11 12:41:01 +00:00
|
|
|
|
2022-12-11 18:41:01 +00:00
|
|
|
bool NGSPICE::loadSpinit( const std::string& aFileName )
|
2016-10-13 11:25:06 +00:00
|
|
|
{
|
|
|
|
if( !wxFileName::FileExists( aFileName ) )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
wxTextFile file;
|
|
|
|
|
|
|
|
if( !file.Open( aFileName ) )
|
|
|
|
return false;
|
|
|
|
|
2022-08-15 17:47:23 +00:00
|
|
|
for( wxString& cmd = file.GetFirstLine(); !file.Eof(); cmd = file.GetNextLine() )
|
2016-10-13 11:25:06 +00:00
|
|
|
Command( cmd.ToStdString() );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-12-11 18:41:01 +00:00
|
|
|
std::string NGSPICE::findCmPath() const
|
2016-10-13 11:25:06 +00:00
|
|
|
{
|
2022-12-11 18:41:01 +00:00
|
|
|
const std::vector<std::string> cmPaths =
|
2016-10-13 11:25:06 +00:00
|
|
|
{
|
2018-03-23 10:14:33 +00:00
|
|
|
#ifdef __WXMAC__
|
2016-10-13 11:25:06 +00:00
|
|
|
"/Applications/ngspice/lib/ngspice",
|
2018-03-23 10:14:33 +00:00
|
|
|
"Contents/Frameworks",
|
2020-07-08 18:29:16 +00:00
|
|
|
wxStandardPaths::Get().GetPluginsDir().ToStdString() + "/sim/ngspice",
|
2021-03-18 19:31:02 +00:00
|
|
|
wxFileName( wxStandardPaths::Get().GetExecutablePath() ).GetPath().ToStdString() +
|
|
|
|
"/../../../../../Contents/PlugIns/sim/ngspice",
|
2019-10-04 01:13:04 +00:00
|
|
|
"../Plugins/sim/ngspice",
|
2021-03-18 19:31:02 +00:00
|
|
|
#endif
|
2023-10-22 16:19:48 +00:00
|
|
|
"../eeschema/ngspice",
|
2016-10-13 11:25:06 +00:00
|
|
|
"../lib/ngspice",
|
2019-10-04 01:13:04 +00:00
|
|
|
"../../lib/ngspice",
|
2016-10-13 11:25:06 +00:00
|
|
|
"lib/ngspice",
|
|
|
|
"ngspice"
|
|
|
|
};
|
|
|
|
|
|
|
|
for( const auto& path : cmPaths )
|
|
|
|
{
|
2018-03-23 10:14:33 +00:00
|
|
|
wxLogTrace( traceNgspice, "ngspice code models search path: %s", path );
|
|
|
|
|
|
|
|
if( wxFileName::FileExists( path + "/spice2poly.cm" ) )
|
|
|
|
{
|
|
|
|
wxLogTrace( traceNgspice, "ngspice code models found in: %s", path );
|
2016-10-13 11:25:06 +00:00
|
|
|
return path;
|
2018-03-23 10:14:33 +00:00
|
|
|
}
|
2016-10-13 11:25:06 +00:00
|
|
|
}
|
|
|
|
|
2022-12-11 18:41:01 +00:00
|
|
|
return std::string();
|
2016-10-13 11:25:06 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2024-05-08 20:39:23 +00:00
|
|
|
bool NGSPICE::setCodemodelsInputPath( const std::string& aPath )
|
|
|
|
{
|
|
|
|
if( !m_ngCM_Input_Path )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
LOCALE_IO c_locale; // ngspice works correctly only with C locale
|
|
|
|
|
|
|
|
m_ngCM_Input_Path( aPath.c_str() );
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-12-11 18:41:01 +00:00
|
|
|
bool NGSPICE::loadCodemodels( const std::string& aPath )
|
2016-10-13 11:25:06 +00:00
|
|
|
{
|
|
|
|
wxArrayString cmFiles;
|
|
|
|
size_t count = wxDir::GetAllFiles( aPath, &cmFiles );
|
|
|
|
|
|
|
|
for( const auto& cm : cmFiles )
|
2022-12-11 18:41:01 +00:00
|
|
|
Command( fmt::format( "codemodel '{}'", cm.ToStdString() ) );
|
2016-10-13 11:25:06 +00:00
|
|
|
|
|
|
|
return count != 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-05 17:49:40 +00:00
|
|
|
int NGSPICE::cbSendChar( char* aWhat, int aId, void* aUser )
|
2016-08-11 12:41:01 +00:00
|
|
|
{
|
2021-05-05 17:49:40 +00:00
|
|
|
NGSPICE* sim = reinterpret_cast<NGSPICE*>( aUser );
|
2016-08-11 12:41:07 +00:00
|
|
|
|
2016-08-11 12:41:19 +00:00
|
|
|
if( sim->m_reporter )
|
2016-08-11 12:41:35 +00:00
|
|
|
{
|
|
|
|
// strip stdout/stderr from the line
|
2021-05-05 17:49:40 +00:00
|
|
|
if( ( strncasecmp( aWhat, "stdout ", 7 ) == 0 )
|
|
|
|
|| ( strncasecmp( aWhat, "stderr ", 7 ) == 0 ) )
|
2022-12-11 18:41:01 +00:00
|
|
|
{
|
2021-05-05 17:49:40 +00:00
|
|
|
aWhat += 7;
|
2022-12-11 18:41:01 +00:00
|
|
|
}
|
2016-08-11 12:41:35 +00:00
|
|
|
|
2021-05-05 17:49:40 +00:00
|
|
|
sim->m_reporter->Report( aWhat );
|
2016-08-11 12:41:35 +00:00
|
|
|
}
|
2016-08-11 12:41:07 +00:00
|
|
|
|
|
|
|
return 0;
|
2016-08-11 12:41:01 +00:00
|
|
|
}
|
|
|
|
|
2016-08-11 12:41:07 +00:00
|
|
|
|
2021-05-05 17:49:40 +00:00
|
|
|
int NGSPICE::cbSendStat( char *aWhat, int aId, void* aUser )
|
2016-08-11 12:41:01 +00:00
|
|
|
{
|
2016-08-11 12:41:19 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-05-05 17:49:40 +00:00
|
|
|
int NGSPICE::cbBGThreadRunning( NG_BOOL aFinished, int aId, void* aUser )
|
2016-08-11 12:41:19 +00:00
|
|
|
{
|
2021-05-05 17:49:40 +00:00
|
|
|
NGSPICE* sim = reinterpret_cast<NGSPICE*>( aUser );
|
2016-08-11 12:41:19 +00:00
|
|
|
|
|
|
|
if( sim->m_reporter )
|
2021-05-05 17:49:40 +00:00
|
|
|
sim->m_reporter->OnSimStateChange( sim, aFinished ? SIM_IDLE : SIM_RUNNING );
|
2016-08-11 12:41:19 +00:00
|
|
|
|
2016-08-11 12:41:07 +00:00
|
|
|
return 0;
|
2016-08-11 12:41:01 +00:00
|
|
|
}
|
2016-08-11 12:42:18 +00:00
|
|
|
|
|
|
|
|
2022-12-11 18:41:01 +00:00
|
|
|
int NGSPICE::cbControlledExit( int aStatus, NG_BOOL aImmediate, NG_BOOL aExitOnQuit, int aId,
|
|
|
|
void* aUser )
|
2016-08-11 12:42:18 +00:00
|
|
|
{
|
|
|
|
// Something went wrong, reload the dll
|
2021-05-05 17:49:40 +00:00
|
|
|
NGSPICE* sim = reinterpret_cast<NGSPICE*>( aUser );
|
2018-03-21 16:22:56 +00:00
|
|
|
sim->m_error = true;
|
2016-08-11 12:42:18 +00:00
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
2016-08-18 16:10:00 +00:00
|
|
|
|
2018-03-21 16:22:56 +00:00
|
|
|
|
|
|
|
void NGSPICE::validate()
|
|
|
|
{
|
|
|
|
if( m_error )
|
|
|
|
{
|
|
|
|
m_initialized = false;
|
|
|
|
init_dll();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2022-05-28 20:40:24 +00:00
|
|
|
void NGSPICE::Clean()
|
|
|
|
{
|
|
|
|
Command( "destroy all" );
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2016-08-18 16:10:00 +00:00
|
|
|
bool NGSPICE::m_initialized = false;
|