7
mirror of https://gitlab.com/kicad/code/kicad.git synced 2025-04-19 00:21:36 +00:00

Add new spice regression test for windows path separators.

This commit is contained in:
Jeff Young 2023-01-21 14:27:33 +00:00
parent 542719c753
commit 6053b86a24
8 changed files with 1161 additions and 252 deletions

View File

@ -0,0 +1,334 @@
{
"board": {
"3dviewports": [],
"design_settings": {
"defaults": {
"board_outline_line_width": 0.1,
"copper_line_width": 0.2,
"copper_text_size_h": 1.5,
"copper_text_size_v": 1.5,
"copper_text_thickness": 0.3,
"other_line_width": 0.15,
"silk_line_width": 0.15,
"silk_text_size_h": 1.0,
"silk_text_size_v": 1.0,
"silk_text_thickness": 0.15
},
"diff_pair_dimensions": [],
"drc_exclusions": [],
"rules": {
"min_copper_edge_clearance": 0.0,
"solder_mask_clearance": 0.0,
"solder_mask_min_width": 0.0
},
"track_widths": [],
"via_dimensions": []
},
"layer_presets": [],
"viewports": []
},
"boards": [],
"cvpcb": {
"equivalence_files": []
},
"erc": {
"erc_exclusions": [],
"meta": {
"version": 0
},
"pin_map": [
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
1,
0,
1,
2
],
[
0,
1,
0,
0,
0,
0,
1,
1,
2,
1,
1,
2
],
[
0,
0,
0,
0,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
0,
2
],
[
1,
1,
1,
1,
1,
0,
1,
1,
1,
1,
1,
2
],
[
0,
0,
0,
1,
0,
0,
1,
0,
0,
0,
0,
2
],
[
0,
2,
1,
2,
0,
0,
1,
0,
2,
2,
2,
2
],
[
0,
2,
0,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
0,
2,
1,
1,
0,
0,
1,
0,
2,
0,
0,
2
],
[
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2,
2
]
],
"rule_severities": {
"bus_definition_conflict": "error",
"bus_entry_needed": "error",
"bus_label_syntax": "error",
"bus_to_bus_conflict": "error",
"bus_to_net_conflict": "error",
"different_unit_footprint": "error",
"different_unit_net": "error",
"duplicate_reference": "error",
"duplicate_sheet_names": "error",
"extra_units": "error",
"global_label_dangling": "warning",
"hier_label_mismatch": "error",
"label_dangling": "error",
"lib_symbol_issues": "warning",
"multiple_net_names": "warning",
"net_not_bus_member": "warning",
"no_connect_connected": "warning",
"no_connect_dangling": "warning",
"pin_not_connected": "error",
"pin_not_driven": "error",
"pin_to_pin": "warning",
"power_pin_not_driven": "error",
"similar_labels": "warning",
"unannotated": "error",
"unit_value_mismatch": "error",
"unresolved_variable": "error",
"wire_dangling": "error"
}
},
"libraries": {
"pinned_footprint_libs": [],
"pinned_symbol_libs": []
},
"meta": {
"filename": "sim2_V6.kicad_pro",
"version": 1
},
"net_settings": {
"classes": [
{
"bus_width": 12.0,
"clearance": 0.2,
"diff_pair_gap": 0.25,
"diff_pair_via_gap": 0.25,
"diff_pair_width": 0.2,
"line_style": 0,
"microvia_diameter": 0.3,
"microvia_drill": 0.1,
"name": "Default",
"pcb_color": "rgba(0, 0, 0, 0.000)",
"schematic_color": "rgba(0, 0, 0, 0.000)",
"track_width": 0.25,
"via_diameter": 0.8,
"via_drill": 0.4,
"wire_width": 6.0
}
],
"meta": {
"version": 2
},
"net_colors": null,
"netclass_assignments": null,
"netclass_patterns": []
},
"pcbnew": {
"last_paths": {
"gencad": "",
"idf": "",
"netlist": "",
"specctra_dsn": "",
"step": "",
"vrml": ""
},
"page_layout_descr_file": ""
},
"schematic": {
"annotate_start_num": 0,
"drawing": {
"dashed_lines_dash_length_ratio": 12.0,
"dashed_lines_gap_length_ratio": 3.0,
"default_line_thickness": 6.0,
"default_text_size": 50.0,
"field_names": [],
"intersheets_ref_own_page": false,
"intersheets_ref_prefix": "",
"intersheets_ref_short": false,
"intersheets_ref_show": false,
"intersheets_ref_suffix": "",
"junction_size_choice": 3,
"label_size_ratio": 0.375,
"pin_symbol_size": 25.0,
"text_offset_ratio": 0.15
},
"legacy_lib_dir": "",
"legacy_lib_list": [],
"meta": {
"version": 1
},
"net_format_name": "",
"ngspice": {
"fix_include_paths": true,
"fix_passive_vals": true,
"meta": {
"version": 0
},
"model_mode": 2,
"workbook_filename": "sim2_V6.wbk"
},
"page_layout_descr_file": "",
"plot_directory": "",
"spice_adjust_passive_values": false,
"spice_external_command": "spice \"%I\"",
"spice_save_all_currents": false,
"spice_save_all_voltages": false,
"subpart_first_id": 65,
"subpart_id_separator": 0
},
"sheets": [
[
"c320d65b-dd35-489c-9c1b-364c21869ec2",
""
]
],
"text_variables": {}
}

View File

LOADING design file

View File

@ -0,0 +1,10 @@
KiCad schematic
.include "/Users/jeff/kicad_arm/kicad/qa/data/eeschema/issue13591_models/diode.lib"
.save all
.probe alli
.tran 1us 1ms
R2 0 Net-_D1-A_ 10k
D1 Net-_D1-A_ 0 1N456
R1 Net-_R1-Pad1_ Net-_D1-A_ 1k
V1 Net-_R1-Pad1_ 0 dc 0 pulse(0 2 1m 50n 50n 1m 2m)
.end

View File

@ -0,0 +1,22 @@
*****
* DIODE.LIB
* spectrum
* andy
* Generated by the Micro-Cap Model program Version 11.0.1.7.
*****
*** Low Leakage Diode
.MODEL 1N456 D (BV=30 CJO=4.505p IBV=100p IS=29.054p M=385.099m N=1.425
+ RL=125.018MEG RS=910.713m TT=5u VJ=700m)
*** Low Leakage Diode
.MODEL 1N456A D (BV=30 CJO=4.505E-12 IBV=100E-12 IS=29.054E-12 M=385.099E-3 N=1.425
+ RS=910.713E-3 TT=5E-6 VJ=700E-3)
*** Low Leakage Diode
.MODEL 1N457 D (BV=70 CJO=4.505p IBV=100p IS=19.191p M=385.099m N=1.388
+ RL=125.012MEG RS=980.164m TT=5u VJ=700m)
*** Low Leakage Diode
.MODEL 1N457A D (BV=70 CJO=4.505p IBV=100p IS=19.191p M=385.099m N=1.388
+ RL=125.018MEG RS=980.164m TT=5u VJ=700m)

View File

@ -96,6 +96,7 @@ if( KICAD_SPICE )
sim/test_library_spice.cpp
sim/test_sim_model_inference.cpp
sim/test_sim_model_ngspice.cpp
sim/test_sim_regressions.cpp
sim/test_ngspice_helpers.cpp
)
endif()

View File

@ -0,0 +1,84 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2023 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifdef KICAD_SPICE
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/results_collector.hpp> // To check if the current test failed (to be moved?).
#include <eeschema_test_utils.h>
#include <test_netlist_exporter_spice.h>
#include <wx/ffile.h>
#include <mock_pgm_base.h>
#include <locale_io.h>
class TEST_SIM_REGRESSIONS_FIXTURE : public TEST_NETLIST_EXPORTER_SPICE_FIXTURE
{
public:
TEST_SIM_REGRESSIONS_FIXTURE() :
TEST_NETLIST_EXPORTER_SPICE_FIXTURE()
{
}
~TEST_SIM_REGRESSIONS_FIXTURE()
{
}
wxFileName GetSchematicPath( const wxString& aBaseName ) override
{
wxFileName fn = KI_TEST::GetEeschemaTestDataDir();
fn.SetName( aBaseName );
fn.SetExt( KiCadSchematicFileExtension );
return fn;
}
wxString GetNetlistPath( bool aTest = false ) override
{
wxFileName netFile = m_schematic.Prj().GetProjectFullName();
if( aTest )
netFile.SetName( netFile.GetName() + "_test" );
netFile.SetExt( "spice" );
return netFile.GetFullPath();
}
};
BOOST_FIXTURE_TEST_SUITE( SimRegressions, TEST_SIM_REGRESSIONS_FIXTURE )
BOOST_AUTO_TEST_CASE( WindowsPaths )
{
LOCALE_IO dummy;
TestNetlist( "issue13591" );
TestTranPoint( 100e-6, { { "I(R1)", 0 }, { "I(R2)", 0 } } );
TestTranPoint( 500e-6, { { "I(R1)", 0 }, { "I(R2)", 0 } } );
}
BOOST_AUTO_TEST_SUITE_END()
#endif // KICAD_SPICE

View File

@ -25,263 +25,12 @@
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/results_collector.hpp> // To check if the current test failed (to be moved?).
#include <eeschema_test_utils.h>
#include <netlist_exporter_spice.h>
#include <sim/ngspice.h>
#include <test_netlist_exporter_spice.h>
#include <sim/spice_reporter.h>
#include <wx/ffile.h>
#include <mock_pgm_base.h>
#include <locale_io.h>
class TEST_NETLIST_EXPORTER_SPICE_FIXTURE : public TEST_NETLIST_EXPORTER_FIXTURE<NETLIST_EXPORTER_SPICE>
{
public:
class SPICE_TEST_REPORTER : public SPICE_REPORTER
{
public:
SPICE_TEST_REPORTER( std::shared_ptr<wxString> aLog ) : m_log( std::move( aLog ) ) {}
REPORTER& Report( const wxString& aText,
SEVERITY aSeverity = RPT_SEVERITY_UNDEFINED ) override
{
*m_log << aText << "\n";
// You can add a debug trace here.
return *this;
}
bool HasMessage() const override { return false; }
void OnSimStateChange( SPICE_SIMULATOR* aObject, SIM_STATE aNewState ) override
{
}
private:
std::shared_ptr<wxString> m_log;
};
TEST_NETLIST_EXPORTER_SPICE_FIXTURE() :
TEST_NETLIST_EXPORTER_FIXTURE<NETLIST_EXPORTER_SPICE>(),
m_simulator( SPICE_SIMULATOR::CreateInstance( "ngspice" ) ),
m_log( std::make_shared<wxString>() ),
m_reporter( std::make_unique<SPICE_TEST_REPORTER>( m_log ) ),
m_abort( false )
{
}
~TEST_NETLIST_EXPORTER_SPICE_FIXTURE()
{
using namespace boost::unit_test;
test_case::id_t id = framework::current_test_case().p_id;
test_results results = results_collector.results( id );
// Output a log if the test has failed.
BOOST_CHECK_MESSAGE( results.passed(), "\nNGSPICE LOG\n===========\n" << *m_log );
}
wxFileName GetSchematicPath( const wxString& aBaseName ) override
{
wxFileName fn = KI_TEST::GetEeschemaTestDataDir();
fn.AppendDir( "spice_netlists" );
fn.AppendDir( aBaseName );
fn.SetName( aBaseName );
fn.SetExt( KiCadSchematicFileExtension );
return fn;
}
wxString GetNetlistPath( bool aTest = false ) override
{
wxFileName netFile = m_schematic.Prj().GetProjectFullName();
if( aTest )
netFile.SetName( netFile.GetName() + "_test" );
netFile.SetExt( "spice" );
return netFile.GetFullPath();
}
void CompareNetlists() override
{
m_abort = false;
// Our simulator is actually Ngspice.
NGSPICE* ngspice = dynamic_cast<NGSPICE*>( m_simulator.get() );
BOOST_REQUIRE( ngspice );
ngspice->SetReporter( m_reporter.get() );
wxFFile file( GetNetlistPath( true ), "rt" );
wxString netlist;
file.ReadAll( &netlist );
//ngspice->Init();
ngspice->Command( "set ngbehavior=ps" );
ngspice->Command( "setseed 1" );
BOOST_REQUIRE( ngspice->LoadNetlist( std::string( netlist.ToUTF8() ) ) );
BOOST_REQUIRE( ngspice->Run() );
// Test if ngspice cannot run a simulation (missing code models).
// in this case the log contains "MIF-ERROR" and/or "Error: circuit not parsed"
// when the simulation is not run the spice command "linearize" crashes.
bool err_found = m_log->Find( wxT( "Error: circuit not parsed" ) ) != wxNOT_FOUND
|| m_log->Find( wxT( "MIF-ERROR" ) ) != wxNOT_FOUND;
if( err_found )
{
if( m_log->Find( wxT( "MIF-ERROR" ) ) != wxNOT_FOUND )
wxLogWarning( wxT( "Cannot run ngspice. test skipped. Missing code model files?" ) );
else
wxLogWarning( wxT( "Cannot run ngspice. test skipped. Install error?" ) );
m_abort = true;
// Still display the original netlist in this case.
*m_log << "Original Netlist\n";
*m_log << "----------------\n";
*m_log << netlist << "\n";
return;
}
// We need to make sure that the number of points always the same.
ngspice->Command( "linearize" );
// Debug info.
// Display all vectors.
ngspice->Command( "echo Available Vectors" );
ngspice->Command( "echo -----------------" );
ngspice->Command( "display" );
// Display the original netlist.
*m_log << "Original Netlist\n";
*m_log << "----------------\n";
*m_log << netlist << "\n";
// Display the expanded netlist.
ngspice->Command( "echo Expanded Netlist" );
ngspice->Command( "echo ----------------" );
ngspice->Command( "listing runnable" );
}
void TestNetlist( const wxString& aBaseName )
{
// We actually ended up only generating the netlist here.
TEST_NETLIST_EXPORTER_FIXTURE<NETLIST_EXPORTER_SPICE>::TestNetlist( aBaseName );
}
void TestOpPoint( double aRefValue, const std::string& aVectorName, double aMaxRelError = 1e-2 )
{
BOOST_TEST_CONTEXT( "Vector name: " << aVectorName )
{
NGSPICE* ngspice = static_cast<NGSPICE*>( m_simulator.get() );
std::vector<double> vector = ngspice->GetRealPlot( aVectorName );
BOOST_REQUIRE_EQUAL( vector.size(), 1 );
double maxError = abs( aRefValue * aMaxRelError );
BOOST_CHECK_LE( abs( vector[0] - aRefValue ), aMaxRelError );
}
}
void TestPoint( const std::string& aXVectorName, double aXValue,
const std::map<const std::string, double> aTestVectorsAndValues,
double aMaxRelError = 1e-2 )
{
// The default aMaxRelError is fairly large because we have some problems with determinism
// in QA pipeline. We don't need to fix this for now because, if this has to be fixed in
// the first place, this has to be done from Ngspice's side.
BOOST_TEST_CONTEXT( "X vector name: " << aXVectorName << ", X value: " << aXValue )
{
NGSPICE* ngspice = static_cast<NGSPICE*>( m_simulator.get() );
std::vector<double> xVector = ngspice->GetRealPlot( aXVectorName );
std::size_t i = 0;
for(; i < xVector.size(); ++i )
{
double inf = std::numeric_limits<double>::infinity();
double leftDelta = ( aXValue - ( i >= 1 ? xVector[i - 1] : -inf ) );
double middleDelta = ( aXValue - xVector[i] );
double rightDelta = ( aXValue - ( i < xVector.size() - 1 ? xVector[i + 1] : inf ) );
// Check if this point is the closest one.
if( abs( middleDelta ) <= abs( leftDelta )
&& abs( middleDelta ) <= abs( rightDelta ) )
{
break;
}
}
BOOST_REQUIRE_LT( i, xVector.size() );
for( auto& [vectorName, refValue] : aTestVectorsAndValues )
{
std::vector<double> yVector = ngspice->GetMagPlot( vectorName );
BOOST_REQUIRE_GE( yVector.size(), i + 1 );
BOOST_TEST_CONTEXT( "Y vector name: " << vectorName
<< ", Ref value: " << refValue
<< ", Actual value: " << yVector[i] )
{
double maxError = abs( refValue * aMaxRelError );
if( maxError == 0 )
{
// If refValue is 0, we need a obtain the max. error differently.
maxError = aMaxRelError;
}
BOOST_CHECK_LE( abs( yVector[i] - refValue ), maxError );
}
}
}
}
void TestTranPoint( double aTime,
const std::map<const std::string, double> aTestVectorsAndValues,
double aMaxRelError = 1e-2 )
{
TestPoint( "time", aTime, aTestVectorsAndValues, aMaxRelError );
}
void TestACPoint( double aFrequency,
const std::map<const std::string, double> aTestVectorsAndValues,
double aMaxRelError = 1e-2 )
{
TestPoint( "frequency", aFrequency, aTestVectorsAndValues, aMaxRelError );
}
wxString GetResultsPath( bool aTest = false )
{
wxFileName netlistPath( GetNetlistPath( aTest ) );
netlistPath.SetExt( "csv" );
return netlistPath.GetFullPath();
}
unsigned GetNetlistOptions() override
{
return NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES
| NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_CURRENTS
| NETLIST_EXPORTER_SPICE::OPTION_ADJUST_INCLUDE_PATHS
| NETLIST_EXPORTER_SPICE::OPTION_SIM_COMMAND;
}
std::shared_ptr<SPICE_SIMULATOR> m_simulator;
std::shared_ptr<wxString> m_log;
std::unique_ptr<SPICE_TEST_REPORTER> m_reporter;
bool m_abort; // set to true to force abort durint a test
};
BOOST_FIXTURE_TEST_SUITE( NetlistExporterSpice, TEST_NETLIST_EXPORTER_SPICE_FIXTURE )

View File

@ -0,0 +1,280 @@
/*
* This program source code file is part of KiCad, a free EDA CAD application.
*
* Copyright (C) 2022 KiCad Developers, see AUTHORS.TXT for contributors.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* 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:
* http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
* or you may search the http://www.gnu.org website for the version 2 license,
* or you may write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
*/
#ifdef KICAD_SPICE
#include <qa_utils/wx_utils/unit_test_utils.h>
#include <boost/test/results_collector.hpp> // To check if the current test failed (to be moved?).
#include <eeschema_test_utils.h>
#include <netlist_exporter_spice.h>
#include <sim/ngspice.h>
#include <sim/spice_reporter.h>
#include <wx/ffile.h>
#include <mock_pgm_base.h>
#include <locale_io.h>
class TEST_NETLIST_EXPORTER_SPICE_FIXTURE : public TEST_NETLIST_EXPORTER_FIXTURE<NETLIST_EXPORTER_SPICE>
{
public:
class SPICE_TEST_REPORTER : public SPICE_REPORTER
{
public:
SPICE_TEST_REPORTER( std::shared_ptr<wxString> aLog ) :
m_log( std::move( aLog ) )
{}
REPORTER& Report( const wxString& aText,
SEVERITY aSeverity = RPT_SEVERITY_UNDEFINED ) override
{
*m_log << aText << "\n";
// You can add a debug trace here.
return *this;
}
bool HasMessage() const override { return false; }
void OnSimStateChange( SPICE_SIMULATOR* aObject, SIM_STATE aNewState ) override { }
private:
std::shared_ptr<wxString> m_log;
};
TEST_NETLIST_EXPORTER_SPICE_FIXTURE() :
TEST_NETLIST_EXPORTER_FIXTURE<NETLIST_EXPORTER_SPICE>(),
m_simulator( SPICE_SIMULATOR::CreateInstance( "ngspice" ) ),
m_log( std::make_shared<wxString>() ),
m_reporter( std::make_unique<SPICE_TEST_REPORTER>( m_log ) ),
m_abort( false )
{
}
~TEST_NETLIST_EXPORTER_SPICE_FIXTURE()
{
using namespace boost::unit_test;
test_case::id_t id = framework::current_test_case().p_id;
test_results results = results_collector.results( id );
// Output a log if the test has failed.
BOOST_CHECK_MESSAGE( results.passed(), "\nNGSPICE LOG\n===========\n" << *m_log );
}
wxFileName GetSchematicPath( const wxString& aBaseName ) override
{
wxFileName fn = KI_TEST::GetEeschemaTestDataDir();
fn.AppendDir( "spice_netlists" );
fn.AppendDir( aBaseName );
fn.SetName( aBaseName );
fn.SetExt( KiCadSchematicFileExtension );
return fn;
}
wxString GetNetlistPath( bool aTest = false ) override
{
wxFileName netFile = m_schematic.Prj().GetProjectFullName();
if( aTest )
netFile.SetName( netFile.GetName() + "_test" );
netFile.SetExt( "spice" );
return netFile.GetFullPath();
}
void CompareNetlists() override
{
m_abort = false;
// Our simulator is actually Ngspice.
NGSPICE* ngspice = dynamic_cast<NGSPICE*>( m_simulator.get() );
BOOST_REQUIRE( ngspice );
ngspice->SetReporter( m_reporter.get() );
wxFFile file( GetNetlistPath( true ), "rt" );
wxString netlist;
file.ReadAll( &netlist );
//ngspice->Init();
ngspice->Command( "set ngbehavior=ps" );
ngspice->Command( "setseed 1" );
BOOST_REQUIRE( ngspice->LoadNetlist( std::string( netlist.ToUTF8() ) ) );
BOOST_REQUIRE( ngspice->Run() );
// Test if ngspice cannot run a simulation (missing code models).
// in this case the log contains "MIF-ERROR" and/or "Error: circuit not parsed"
// when the simulation is not run the spice command "linearize" crashes.
bool err_found = m_log->Find( wxT( "Error: circuit not parsed" ) ) != wxNOT_FOUND
|| m_log->Find( wxT( "MIF-ERROR" ) ) != wxNOT_FOUND;
if( err_found )
{
if( m_log->Find( wxT( "MIF-ERROR" ) ) != wxNOT_FOUND )
wxLogWarning( wxT( "Cannot run ngspice. test skipped. Missing code model files?" ) );
else
wxLogWarning( wxT( "Cannot run ngspice. test skipped. Install error?" ) );
m_abort = true;
// Still display the original netlist in this case.
*m_log << "Original Netlist\n";
*m_log << "----------------\n";
*m_log << netlist << "\n";
return;
}
// We need to make sure that the number of points always the same.
ngspice->Command( "linearize" );
// Debug info.
// Display all vectors.
ngspice->Command( "echo Available Vectors" );
ngspice->Command( "echo -----------------" );
ngspice->Command( "display" );
// Display the original netlist.
*m_log << "Original Netlist\n";
*m_log << "----------------\n";
*m_log << netlist << "\n";
// Display the expanded netlist.
ngspice->Command( "echo Expanded Netlist" );
ngspice->Command( "echo ----------------" );
ngspice->Command( "listing runnable" );
}
void TestOpPoint( double aRefValue, const std::string& aVectorName, double aMaxRelError = 1e-2 )
{
BOOST_TEST_CONTEXT( "Vector name: " << aVectorName )
{
NGSPICE* ngspice = static_cast<NGSPICE*>( m_simulator.get() );
std::vector<double> vector = ngspice->GetRealPlot( aVectorName );
BOOST_REQUIRE_EQUAL( vector.size(), 1 );
double maxError = abs( aRefValue * aMaxRelError );
BOOST_CHECK_LE( abs( vector[0] - aRefValue ), aMaxRelError );
}
}
void TestPoint( const std::string& aXVectorName, double aXValue,
const std::map<const std::string, double> aTestVectorsAndValues,
double aMaxRelError = 1e-2 )
{
// The default aMaxRelError is fairly large because we have some problems with determinism
// in QA pipeline. We don't need to fix this for now because, if this has to be fixed in
// the first place, this has to be done from Ngspice's side.
BOOST_TEST_CONTEXT( "X vector name: " << aXVectorName << ", X value: " << aXValue )
{
NGSPICE* ngspice = static_cast<NGSPICE*>( m_simulator.get() );
std::vector<double> xVector = ngspice->GetRealPlot( aXVectorName );
std::size_t i = 0;
for(; i < xVector.size(); ++i )
{
double inf = std::numeric_limits<double>::infinity();
double leftDelta = ( aXValue - ( i >= 1 ? xVector[i - 1] : -inf ) );
double middleDelta = ( aXValue - xVector[i] );
double rightDelta = ( aXValue - ( i < xVector.size() - 1 ? xVector[i + 1] : inf ) );
// Check if this point is the closest one.
if( abs( middleDelta ) <= abs( leftDelta )
&& abs( middleDelta ) <= abs( rightDelta ) )
{
break;
}
}
BOOST_REQUIRE_LT( i, xVector.size() );
for( auto& [vectorName, refValue] : aTestVectorsAndValues )
{
std::vector<double> yVector = ngspice->GetMagPlot( vectorName );
BOOST_REQUIRE_GE( yVector.size(), i + 1 );
BOOST_TEST_CONTEXT( "Y vector name: " << vectorName
<< ", Ref value: " << refValue
<< ", Actual value: " << yVector[i] )
{
double maxError = abs( refValue * aMaxRelError );
if( maxError == 0 )
{
// If refValue is 0, we need a obtain the max. error differently.
maxError = aMaxRelError;
}
BOOST_CHECK_LE( abs( yVector[i] - refValue ), maxError );
}
}
}
}
void TestTranPoint( double aTime,
const std::map<const std::string, double> aTestVectorsAndValues,
double aMaxRelError = 1e-2 )
{
TestPoint( "time", aTime, aTestVectorsAndValues, aMaxRelError );
}
void TestACPoint( double aFrequency,
const std::map<const std::string, double> aTestVectorsAndValues,
double aMaxRelError = 1e-2 )
{
TestPoint( "frequency", aFrequency, aTestVectorsAndValues, aMaxRelError );
}
wxString GetResultsPath( bool aTest = false )
{
wxFileName netlistPath( GetNetlistPath( aTest ) );
netlistPath.SetExt( "csv" );
return netlistPath.GetFullPath();
}
unsigned GetNetlistOptions() override
{
return NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_VOLTAGES
| NETLIST_EXPORTER_SPICE::OPTION_SAVE_ALL_CURRENTS
| NETLIST_EXPORTER_SPICE::OPTION_ADJUST_INCLUDE_PATHS
| NETLIST_EXPORTER_SPICE::OPTION_SIM_COMMAND;
}
std::shared_ptr<SPICE_SIMULATOR> m_simulator;
std::shared_ptr<wxString> m_log;
std::unique_ptr<SPICE_TEST_REPORTER> m_reporter;
bool m_abort; // set to true to force abort durint a test
};
#endif // KICAD_SPICE