KiCad PCB EDA Suite
Loading...
Searching...
No Matches
netlist_exporter_spice.cpp
Go to the documentation of this file.
1/*
2 * This program source code file is part of KiCad, a free EDA CAD application.
3 *
4 * Copyright (C) 1992-2013 jp.charras at wanadoo.fr
5 * Copyright (C) 2013 SoftPLC Corporation, Dick Hollenbeck <[email protected]>
6 * Copyright (C) 1992-2023 KiCad Developers, see AUTHORS.TXT for contributors.
7 *
8 * This program is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2
11 * of the License, or (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, you may find one here:
20 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
21 * or you may search the http://www.gnu.org website for the version 2 license,
22 * or you may write to the Free Software Foundation, Inc.,
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
24 */
25
26#include <sim/kibis/kibis.h>
31#include <sim/sim_model_ideal.h>
32#include <common.h>
33#include <confirm.h>
34#include <pgm_base.h>
35#include <env_paths.h>
36#include <sim/sim_library.h>
38#include <sim/sim_model_kibis.h>
40#include <sch_screen.h>
41#include <sch_text.h>
42#include <sch_textbox.h>
43#include <string_utils.h>
44
46#include <fmt/core.h>
47#include <paths.h>
48#include <wx/dir.h>
49#include <wx/log.h>
50#include <locale_io.h>
51#include "markup_parser.h"
52
53#if 0
54
55#include <pegtl.hpp>
56#include <pegtl/contrib/parse_tree.hpp>
57
58namespace NETLIST_EXPORTER_SPICE_PARSER
59{
60 using namespace SPICE_GRAMMAR;
61
62 struct textGrammar : must<spiceSource> {};
63
64 template <typename Rule> struct textSelector : std::false_type {};
65 template <> struct textSelector<modelUnit> : std::true_type {};
66
67 template <> struct textSelector<dotControl> : std::true_type {};
68
69 template <> struct textSelector<dotTitle> : std::true_type {};
70 template <> struct textSelector<dotTitleTitle> : std::true_type {};
71
72 template <> struct textSelector<dotInclude> : std::true_type {};
73 template <> struct textSelector<dotIncludePathWithoutQuotes> : std::true_type {};
74 template <> struct textSelector<dotIncludePathWithoutApostrophes> : std::true_type {};
75 template <> struct textSelector<dotIncludePath> : std::true_type {};
76
77 template <> struct textSelector<kLine> : std::true_type {};
78
79 template <> struct textSelector<dotLine> : std::true_type {};
80}
81#endif
82
83
84std::string NAME_GENERATOR::Generate( const std::string& aProposedName )
85{
86 std::string name = aProposedName;
87 int ii = 1;
88
89 while( m_names.count( name ) )
90 name = fmt::format( "{}#{}", aProposedName, ii++ );
91
92 return name;
93}
94
95
97 wxWindow* aDialogParent ) :
98 NETLIST_EXPORTER_BASE( aSchematic ),
99 m_libMgr( &aSchematic->Prj() ),
100 m_dialogParent( aDialogParent )
101{
102}
103
104
105bool NETLIST_EXPORTER_SPICE::WriteNetlist( const wxString& aOutFileName, unsigned aNetlistOptions,
106 REPORTER& aReporter )
107{
108 FILE_OUTPUTFORMATTER formatter( aOutFileName, wxT( "wt" ), '\'' );
109 return DoWriteNetlist( wxEmptyString, aNetlistOptions, formatter, aReporter );
110}
111
112
113bool NETLIST_EXPORTER_SPICE::DoWriteNetlist( const wxString& aSimCommand, unsigned aSimOptions,
114 OUTPUTFORMATTER& aFormatter, REPORTER& aReporter )
115{
117
118 // Cleanup list to avoid duplicate if the netlist exporter is run more than once.
119 m_rawIncludes.clear();
120
121 bool result = ReadSchematicAndLibraries( aSimOptions, aReporter );
122
123 WriteHead( aFormatter, aSimOptions );
124
125 writeIncludes( aFormatter, aSimOptions );
126 writeModels( aFormatter );
127
128 // Skip this if there is no netlist to avoid an ngspice segfault
129 if( !m_items.empty() )
130 WriteDirectives( aSimCommand, aSimOptions, aFormatter );
131
132 writeItems( aFormatter );
133
134 WriteTail( aFormatter, aSimOptions );
135
136 return result;
137}
138
139
140void NETLIST_EXPORTER_SPICE::WriteHead( OUTPUTFORMATTER& aFormatter, unsigned aNetlistOptions )
141{
142 aFormatter.Print( 0, ".title KiCad schematic\n" );
143}
144
145
146void NETLIST_EXPORTER_SPICE::WriteTail( OUTPUTFORMATTER& aFormatter, unsigned aNetlistOptions )
147{
148 aFormatter.Print( 0, ".end\n" );
149}
150
151
153 REPORTER& aReporter )
154{
155 wxString msg;
156 std::set<std::string> refNames; // Set of reference names to check for duplication.
157 int ncCounter = 1;
158
159 ReadDirectives( aNetlistOptions );
160
161 m_nets.clear();
162 m_items.clear();
164 m_libParts.clear();
165
166 wxFileName cacheDir;
167 cacheDir.AssignDir( PATHS::GetUserCachePath() );
168 cacheDir.AppendDir( wxT( "ibis" ) );
169
170 if( !cacheDir.DirExists() )
171 {
172 cacheDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
173
174 if( !cacheDir.DirExists() )
175 {
176 wxLogTrace( wxT( "IBIS_CACHE:" ),
177 wxT( "%s:%s:%d\n * failed to create ibis cache directory '%s'" ),
178 __FILE__, __FUNCTION__, __LINE__, cacheDir.GetPath() );
179
180 return false;
181 }
182 }
183
184 wxDir dir;
185 wxString dirName = cacheDir.GetFullPath();
186
187 if( !dir.Open( dirName ) )
188 return false;
189
190 wxFileName thisFile;
191 wxArrayString fileList;
192 wxString fileSpec = wxT( "*.cache" );
193
194 thisFile.SetPath( dirName ); // Set the base path to the cache folder
195
196 size_t numFilesFound = wxDir::GetAllFiles( dirName, &fileList, fileSpec );
197
198 for( size_t ii = 0; ii < numFilesFound; ii++ )
199 {
200 // Completes path to specific file so we can get its "last access" date
201 thisFile.SetFullName( fileList[ii] );
202 wxRemoveFile( thisFile.GetFullPath() );
203 }
204
205 for( SCH_SHEET_PATH& sheet : GetSheets( aNetlistOptions ) )
206 {
207 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
208 {
209 SCH_SYMBOL* symbol = findNextSymbol( item, &sheet );
210
211 if( !symbol || symbol->GetExcludedFromSim() )
212 continue;
213
214 SPICE_ITEM spiceItem;
215 std::vector<PIN_INFO> pins = CreatePinList( symbol, &sheet, true );
216
217 for( const SCH_FIELD& field : symbol->GetFields() )
218 {
219 spiceItem.fields.emplace_back( VECTOR2I(), -1, symbol, field.GetName() );
220
221 if( field.GetId() == REFERENCE_FIELD )
222 spiceItem.fields.back().SetText( symbol->GetRef( &sheet ) );
223 else
224 spiceItem.fields.back().SetText( field.GetShownText( &sheet, false ) );
225 }
226
227 readRefName( sheet, *symbol, spiceItem, refNames );
228 readModel( sheet, *symbol, spiceItem, aReporter );
229 readPinNumbers( *symbol, spiceItem, pins );
230 readPinNetNames( *symbol, spiceItem, pins, ncCounter );
231 readNodePattern( spiceItem );
232 // TODO: transmission line handling?
233
234 m_items.push_back( std::move( spiceItem ) );
235 }
236 }
237
238 return !aReporter.HasMessage();
239}
240
241
243{
244 MARKUP::MARKUP_PARSER markupParser( aNetName );
245 std::unique_ptr<MARKUP::NODE> root = markupParser.Parse();
246 std::string converted;
247
248 std::function<void( const std::unique_ptr<MARKUP::NODE>&)> convertMarkup =
249 [&]( const std::unique_ptr<MARKUP::NODE>& aNode )
250 {
251 if( aNode )
252 {
253 if( !aNode->is_root() )
254 {
255 if( aNode->isOverbar() )
256 {
257 // ~{CLK} is a different signal than CLK
258 converted += '~';
259 }
260 else if( aNode->isSubscript() || aNode->isSuperscript() )
261 {
262 // V_{OUT} is just a pretty-printed version of VOUT
263 }
264
265 if( aNode->has_content() )
266 converted += aNode->string();
267 }
268
269 for( const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
270 convertMarkup( child );
271 }
272 };
273
274 convertMarkup( root );
275
276 // Replace all ngspice-disallowed chars in netnames by a '_'
277 std::replace( converted.begin(), converted.end(), '%', '_' );
278 std::replace( converted.begin(), converted.end(), '(', '_' );
279 std::replace( converted.begin(), converted.end(), ')', '_' );
280 std::replace( converted.begin(), converted.end(), ',', '_' );
281 std::replace( converted.begin(), converted.end(), '[', '_' );
282 std::replace( converted.begin(), converted.end(), ']', '_' );
283 std::replace( converted.begin(), converted.end(), '<', '_' );
284 std::replace( converted.begin(), converted.end(), '>', '_' );
285 std::replace( converted.begin(), converted.end(), '~', '_' );
286 std::replace( converted.begin(), converted.end(), ' ', '_' );
287
288 aNetName = converted;
289}
290
291
292std::string NETLIST_EXPORTER_SPICE::GetItemName( const std::string& aRefName ) const
293{
294 const SPICE_ITEM* item = FindItem( aRefName );
295
296 if( !item )
297 return "";
298
299 return item->model->SpiceGenerator().ItemName( *item );
300}
301
302
303const SPICE_ITEM* NETLIST_EXPORTER_SPICE::FindItem( const std::string& aRefName ) const
304{
305 const std::list<SPICE_ITEM>& spiceItems = GetItems();
306
307 auto it = std::find_if( spiceItems.begin(), spiceItems.end(),
308 [aRefName]( const SPICE_ITEM& item )
309 {
310 return item.refName == aRefName;
311 } );
312
313 if( it != spiceItems.end() )
314 return &*it;
315
316 return nullptr;
317}
318
319
320void NETLIST_EXPORTER_SPICE::ReadDirectives( unsigned aNetlistOptions )
321{
322 wxString msg;
323 wxString text;
324
325 m_directives.clear();
326
327 for( const SCH_SHEET_PATH& sheet : GetSheets( aNetlistOptions ) )
328 {
329 for( SCH_ITEM* item : sheet.LastScreen()->Items() )
330 {
331 if( item->GetExcludedFromSim() )
332 continue;
333
334 if( item->Type() == SCH_TEXT_T )
335 text = static_cast<SCH_TEXT*>( item )->GetShownText( &sheet, false );
336 else if( item->Type() == SCH_TEXTBOX_T )
337 text = static_cast<SCH_TEXTBOX*>( item )->GetShownText( &sheet, false );
338 else
339 continue;
340
341 // Send anything that contains directives to SPICE
342 wxStringTokenizer tokenizer( text, wxT( "\r\n" ), wxTOKEN_STRTOK );
343 bool foundDirective = false;
344
345 auto isDirective =
346 []( const wxString& line, const wxString& dir )
347 {
348 return line == dir || line.StartsWith( dir + wxS( " " ) );
349 };
350
351 while( tokenizer.HasMoreTokens() )
352 {
353 wxString line = tokenizer.GetNextToken().Upper();
354
355 if( line.StartsWith( wxT( "." ) ) )
356 {
357 if( isDirective( line, wxS( ".AC" ) )
358 || isDirective( line, wxS( ".CONTROL" ) )
359 || isDirective( line, wxS( ".CSPARAM" ) )
360 || isDirective( line, wxS( ".DISTO" ) )
361 || isDirective( line, wxS( ".DC" ) )
362 || isDirective( line, wxS( ".ELSE" ) )
363 || isDirective( line, wxS( ".ELSEIF" ) )
364 || isDirective( line, wxS( ".END" ) )
365 || isDirective( line, wxS( ".ENDC" ) )
366 || isDirective( line, wxS( ".ENDIF" ) )
367 || isDirective( line, wxS( ".ENDS" ) )
368 || isDirective( line, wxS( ".FOUR" ) )
369 || isDirective( line, wxS( ".FUNC" ) )
370 || isDirective( line, wxS( ".GLOBAL" ) )
371 || isDirective( line, wxS( ".IC" ) )
372 || isDirective( line, wxS( ".IF" ) )
373 || isDirective( line, wxS( ".INCLUDE" ) )
374 || isDirective( line, wxS( ".LIB" ) )
375 || isDirective( line, wxS( ".MEAS" ) )
376 || isDirective( line, wxS( ".MODEL" ) )
377 || isDirective( line, wxS( ".NODESET" ) )
378 || isDirective( line, wxS( ".NOISE" ) )
379 || isDirective( line, wxS( ".OP" ) )
380 || isDirective( line, wxS( ".OPTIONS" ) )
381 || isDirective( line, wxS( ".PARAM" ) )
382 || isDirective( line, wxS( ".PLOT" ) )
383 || isDirective( line, wxS( ".PRINT" ) )
384 || isDirective( line, wxS( ".PROBE" ) )
385 || isDirective( line, wxS( ".PZ" ) )
386 || isDirective( line, wxS( ".SAVE" ) )
387 || isDirective( line, wxS( ".SENS" ) )
388 || isDirective( line, wxS( ".SP" ) )
389 || isDirective( line, wxS( ".SUBCKT" ) )
390 || isDirective( line, wxS( ".TEMP" ) )
391 || isDirective( line, wxS( ".TF" ) )
392 || isDirective( line, wxS( ".TITLE" ) )
393 || isDirective( line, wxS( ".TRAN" ) )
394 || isDirective( line, wxS( ".WIDTH" ) ) )
395 {
396 foundDirective = true;
397 break;
398 }
399 }
400 else if( line.StartsWith( wxT( "K" ) ) )
401 {
402 // Check for mutual inductor declaration
403 wxStringTokenizer line_t( line, wxT( " \t" ), wxTOKEN_STRTOK );
404
405 // Coupling ID
406 if( !line_t.HasMoreTokens() || !line_t.GetNextToken().StartsWith( wxT( "K" ) ) )
407 continue;
408
409 // Inductor 1 ID
410 if( !line_t.HasMoreTokens() || !line_t.GetNextToken().StartsWith( wxT( "L" ) ) )
411 continue;
412
413 // Inductor 2 ID
414 if( !line_t.HasMoreTokens() || !line_t.GetNextToken().StartsWith( wxT( "L" ) ) )
415 continue;
416
417 // That's probably distinctive enough not to bother trying to parse the
418 // coupling value. If there's anything else, assume it's the value.
419 if( line_t.HasMoreTokens() )
420 {
421 foundDirective = true;
422 break;
423 }
424 }
425 }
426
427 if( foundDirective )
428 m_directives.emplace_back( text );
429 }
430 }
431}
432
433
435 SPICE_ITEM& aItem, std::set<std::string>& aRefNames )
436{
437 aItem.refName = aSymbol.GetRef( &aSheet );
438
439 if( !aRefNames.insert( aItem.refName ).second )
440 wxASSERT( wxT( "Duplicate refdes encountered; what happened to ReadyToNetlist()?" ) );
441}
442
443
445 SPICE_ITEM& aItem, REPORTER& aReporter )
446{
447 SIM_LIBRARY::MODEL libModel = m_libMgr.CreateModel( &aSheet, aSymbol, aReporter );
448
449 aItem.baseModelName = libModel.name;
450 aItem.model = &libModel.model;
451
452 std::string modelName = aItem.model->SpiceGenerator().ModelName( aItem );
453 // Resolve model name collisions.
455
456 // FIXME: Don't have special cases for raw Spice models and KIBIS.
457 if( auto rawSpiceModel = dynamic_cast<const SIM_MODEL_RAW_SPICE*>( aItem.model ) )
458 {
459 int libParamIndex = static_cast<int>( SIM_MODEL_RAW_SPICE::SPICE_PARAM::LIB );
460 wxString path = rawSpiceModel->GetParam( libParamIndex ).value;
461
462 if( !path.IsEmpty() )
463 m_rawIncludes.insert( path );
464 }
465 else if( auto kibisModel = dynamic_cast<const SIM_MODEL_KIBIS*>( aItem.model ) )
466 {
467 wxFileName cacheFn;
468 cacheFn.AssignDir( PATHS::GetUserCachePath() );
469 cacheFn.AppendDir( wxT( "ibis" ) );
470 cacheFn.SetFullName( aSymbol.GetRef( &aSheet ) + wxT( ".cache" ) );
471
472 wxFile cacheFile( cacheFn.GetFullPath(), wxFile::write );
473
474 if( !cacheFile.IsOpened() )
475 {
476 DisplayErrorMessage( m_dialogParent, wxString::Format( _( "Could not open file '%s' "
477 "to write IBIS model" ),
478 cacheFn.GetFullPath() ) );
479 }
480
481 auto spiceGenerator = static_cast<const SPICE_GENERATOR_KIBIS&>( kibisModel->SpiceGenerator() );
482 std::string modelData = spiceGenerator.IbisDevice(
483 aItem, m_schematic->Prj(),
484 cacheFn.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR ), aReporter );
485
486 cacheFile.Write( wxString( modelData ) );
487 m_rawIncludes.insert( cacheFn.GetFullPath() );
488 }
489}
490
491
493 const std::vector<PIN_INFO>& aPins )
494{
495 for( const PIN_INFO& pin : aPins )
496 aItem.pinNumbers.emplace_back( pin.num.ToStdString() );
497}
498
499
501 const std::vector<PIN_INFO>& aPins, int& aNcCounter )
502{
503 for( const PIN_INFO& pinInfo : aPins )
504 {
505 std::string netName = GenerateItemPinNetName( pinInfo.netName.ToStdString(), aNcCounter );
506
507 aItem.pinNetNames.push_back( netName );
508 m_nets.insert( netName );
509 }
510}
512 std::vector<std::string>& aModifiers )
513{
514 std::string input = SIM_MODEL::GetFieldValue( &aItem.fields, SIM_NODES_FORMAT_FIELD, true );
515
516 if( input == "" )
517 return;
518
519 tao::pegtl::string_input<> in( input, "Sim.NodesFormat field" );
520 std::unique_ptr<tao::pegtl::parse_tree::node> root;
521 std::string singleNodeModifier;
522
523 try
524 {
525 root = tao::pegtl::parse_tree::parse<SIM_XSPICE_PARSER_GRAMMAR::nodeSequenceGrammar,
527 tao::pegtl::nothing,
529 for( const auto& node : root->children )
530 {
531 if( node->is_type<SIM_XSPICE_PARSER_GRAMMAR::squareBracketC>() )
532 {
533 //we want ']' to close previous ?
534 aModifiers.back().append( node->string() );
535 }
536 else
537 { //rest goes to the new singleNodeModifier
538 singleNodeModifier.append( node->string() );
539 }
540
541 if( node->is_type<SIM_XSPICE_PARSER_GRAMMAR::nodeName>() )
542 {
543 aModifiers.push_back( singleNodeModifier );
544 singleNodeModifier.erase( singleNodeModifier.begin(), singleNodeModifier.end() );
545 }
546 }
547 }
548 catch( const tao::pegtl::parse_error& e )
549 {
550 THROW_IO_ERROR( wxString::Format( _( "Error in parsing model '%s', error: '%s'" ),
551 aItem.refName, e.what() ) );
552 }
553}
555{
556 std::vector<std::string> xspicePattern;
557 NETLIST_EXPORTER_SPICE::getNodePattern( aItem, xspicePattern );
558
559 if( xspicePattern.empty() )
560 return;
561
562 if( xspicePattern.size() != aItem.pinNetNames.size() )
563 {
564 THROW_IO_ERROR( wxString::Format( _( "Error in parsing model '%s', wrong number of nodes "
565 "'?' in Sim.NodesFormat compared to connections" ),
566 aItem.refName ) );
567 return;
568 }
569
570 auto itNetNames = aItem.pinNetNames.begin();
571
572 for( std::string& pattern : xspicePattern )
573 {
574 // ngspice does not care about aditional spaces, and we make sure that "%d?" is separated
575 const std::string netName = " " + *itNetNames + " ";
576 pattern.replace( pattern.find( "?" ), 1, netName );
577 *itNetNames = pattern;
578 ++itNetNames;
579 }
580}
581
582void NETLIST_EXPORTER_SPICE::writeInclude( OUTPUTFORMATTER& aFormatter, unsigned aNetlistOptions,
583 const wxString& aPath )
584{
585 // First, expand env vars, if any.
586 wxString expandedPath = ExpandEnvVarSubstitutions( aPath, &m_schematic->Prj() );
587
588 // Path may have been authored by someone on a Windows box; convert it to UNIX format
589 expandedPath.Replace( '\\', '/' );
590
591 wxString fullPath;
592
593 if( aNetlistOptions & OPTION_ADJUST_INCLUDE_PATHS )
594 {
595 // Look for the library in known search locations.
596 fullPath = ResolveFile( expandedPath, &Pgm().GetLocalEnvVariables(), &m_schematic->Prj() );
597
598 if( fullPath.IsEmpty() )
599 {
600 DisplayErrorMessage( m_dialogParent, wxString::Format( _( "Could not find library file "
601 "'%s'" ),
602 expandedPath ) );
603 fullPath = expandedPath;
604 }
605 else if( wxFileName::GetPathSeparator() == '\\' )
606 {
607 // Convert it to UNIX format (again) if ResolveFile() returned a Windows style path
608 fullPath.Replace( '\\', '/' );
609 }
610 }
611 else
612 {
613 fullPath = expandedPath;
614 }
615
616 aFormatter.Print( 0, ".include \"%s\"\n", TO_UTF8( fullPath ) );
617}
618
619
620void NETLIST_EXPORTER_SPICE::writeIncludes( OUTPUTFORMATTER& aFormatter, unsigned aNetlistOptions )
621{
622 for( const auto& [path, library] : m_libMgr.GetLibraries() )
623 {
624 if( dynamic_cast<const SIM_LIBRARY_SPICE*>( &library.get() ) )
625 writeInclude( aFormatter, aNetlistOptions, path );
626 }
627
628 for( const wxString& path : m_rawIncludes )
629 writeInclude( aFormatter, aNetlistOptions, path );
630}
631
632
634{
635 for( const SPICE_ITEM& item : m_items )
636 {
637 if( !item.model->IsEnabled() )
638 continue;
639
640 aFormatter.Print( 0, "%s", item.model->SpiceGenerator().ModelLine( item ).c_str() );
641 }
642}
643
644
646{
647 for( const SPICE_ITEM& item : m_items )
648 {
649 if( !item.model->IsEnabled() )
650 continue;
651
652 aFormatter.Print( 0, "%s", item.model->SpiceGenerator().ItemLine( item ).c_str() );
653 }
654}
655
656
657void NETLIST_EXPORTER_SPICE::WriteDirectives( const wxString& aSimCommand, unsigned aSimOptions,
658 OUTPUTFORMATTER& aFormatter ) const
659{
660 if( aSimOptions & OPTION_SAVE_ALL_VOLTAGES )
661 aFormatter.Print( 0, ".save all\n" );
662
663 if( aSimOptions & OPTION_SAVE_ALL_CURRENTS )
664 aFormatter.Print( 0, ".probe alli\n" );
665
666 if( aSimOptions & OPTION_SAVE_ALL_DISSIPATIONS )
667 {
668 for( const SPICE_ITEM& item : m_items )
669 {
670 // ngspice (v39) does not support power measurement for XSPICE devices
671 // XPSICE devices are marked with 'A'
672 std::string itemName = item.model->SpiceGenerator().ItemName( item );
673
674 if( ( item.model->GetPinCount() >= 2 ) && ( itemName.size() > 0 )
675 && ( itemName.c_str()[0] != 'A' ) )
676 {
677 aFormatter.Print( 0, ".probe p(%s)\n", itemName.c_str() );
678 }
679 }
680 }
681
682 auto isSimCommand =
683 []( const wxString& candidate, const wxString& dir )
684 {
685 return candidate == dir || candidate.StartsWith( dir + wxS( " " ) );
686 };
687
688 for( const wxString& directive : m_directives )
689 {
690 bool simCommand = false;
691
692 if( directive.StartsWith( "." ) )
693 {
694 wxString candidate = directive.Upper();
695
696 simCommand = ( isSimCommand( candidate, wxS( ".AC" ) )
697 || isSimCommand( candidate, wxS( ".DC" ) )
698 || isSimCommand( candidate, wxS( ".TRAN" ) )
699 || isSimCommand( candidate, wxS( ".OP" ) )
700 || isSimCommand( candidate, wxS( ".DISTO" ) )
701 || isSimCommand( candidate, wxS( ".NOISE" ) )
702 || isSimCommand( candidate, wxS( ".PZ" ) )
703 || isSimCommand( candidate, wxS( ".SENS" ) )
704 || isSimCommand( candidate, wxS( ".TF" ) ) );
705 }
706
707 if( !simCommand || ( aSimOptions & OPTION_SIM_COMMAND ) )
708 aFormatter.Print( 0, "%s\n", UTF8( directive ).c_str() );
709 }
710}
711
712
713std::string NETLIST_EXPORTER_SPICE::GenerateItemPinNetName( const std::string& aNetName,
714 int& aNcCounter ) const
715{
716 std::string netName = aNetName;
717
718 ConvertToSpiceMarkup( netName );
719 netName = std::string( UnescapeString( netName ).ToUTF8() );
720
721 if( netName == "" )
722 netName = fmt::format( "NC-{}", aNcCounter++ );
723
724 return netName;
725}
726
727
728SCH_SHEET_LIST NETLIST_EXPORTER_SPICE::GetSheets( unsigned aNetlistOptions ) const
729{
730 if( aNetlistOptions & OPTION_CUR_SHEET_AS_ROOT )
732 else
733 return m_schematic->GetSheets();
734}
735
const char * name
Definition: DXF_plotter.cpp:57
Used for text file output.
Definition: richio.h:475
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:49
std::unique_ptr< NODE > Parse()
std::unordered_set< std::string > m_names
std::string Generate(const std::string &aProposedName)
An abstract class used for the netlist exporters that Eeschema supports.
std::set< LIB_SYMBOL *, LIB_SYMBOL_LESS_THAN > m_libParts
unique library symbols used. LIB_SYMBOL items are sorted by names
std::vector< PIN_INFO > CreatePinList(SCH_SYMBOL *aSymbol, SCH_SHEET_PATH *aSheetPath, bool aKeepUnconnectedPins)
Find a symbol from the DrawList and builds its pin list.
UNIQUE_STRINGS m_referencesAlreadyFound
Used for "multiple symbols per package" symbols to avoid processing a lib symbol more than once.
SCH_SYMBOL * findNextSymbol(EDA_ITEM *aItem, SCH_SHEET_PATH *aSheetPath)
Check if the given symbol should be processed for netlisting.
SCHEMATIC_IFACE * m_schematic
The schematic we're generating a netlist for.
void readPinNetNames(SCH_SYMBOL &aSymbol, SPICE_ITEM &aItem, const std::vector< PIN_INFO > &aPins, int &aNcCounter)
void writeModels(OUTPUTFORMATTER &aFormatter)
void writeIncludes(OUTPUTFORMATTER &aFormatter, unsigned aNetlistOptions)
NETLIST_EXPORTER_SPICE(SCHEMATIC_IFACE *aSchematic, wxWindow *aDialogParent=nullptr)
std::list< SPICE_ITEM > m_items
void getNodePattern(SPICE_ITEM &aItem, std::vector< std::string > &aModifiers)
void ReadDirectives(unsigned aNetlistOptions)
std::string GetItemName(const std::string &aRefName) const
Return name of Spice device corresponding to a schematic symbol.
void readModel(SCH_SHEET_PATH &aSheet, SCH_SYMBOL &aSymbol, SPICE_ITEM &aItem, REPORTER &aReporter)
SCH_SHEET_LIST GetSheets(unsigned aNetlistOptions=0) const
Return the paths of exported sheets (either all or the current one).
void readRefName(SCH_SHEET_PATH &aSheet, SCH_SYMBOL &aSymbol, SPICE_ITEM &aItem, std::set< std::string > &aRefNames)
static void ConvertToSpiceMarkup(std::string &aNetName)
Remove formatting wrappers and replace illegal spice net name characters with underscores.
void writeItems(OUTPUTFORMATTER &aFormatter)
std::vector< wxString > m_directives
Spice directives found in the schematic sheet.
virtual void WriteHead(OUTPUTFORMATTER &aFormatter, unsigned aNetlistOptions)
Write the netlist head (title and so on).
virtual std::string GenerateItemPinNetName(const std::string &aNetName, int &aNcCounter) const
const SPICE_ITEM * FindItem(const std::string &aRefName) const
Find and return the item corresponding to aRefName.
void writeInclude(OUTPUTFORMATTER &aFormatter, unsigned aNetlistOptions, const wxString &aPath)
virtual void WriteDirectives(const wxString &aSimCommand, unsigned aSimOptions, OUTPUTFORMATTER &candidate) const
SIM_LIB_MGR m_libMgr
Holds libraries and models.
virtual bool ReadSchematicAndLibraries(unsigned aNetlistOptions, REPORTER &aReporter)
Process the schematic and Spice libraries to create net mapping and a list of SPICE_ITEMs.
bool DoWriteNetlist(const wxString &aSimCommand, unsigned aSimOptions, OUTPUTFORMATTER &aFormatter, REPORTER &aReporter)
Write the netlist in aFormatter.
std::set< std::string > m_nets
Items representing schematic symbols in Spice world.
void readPinNumbers(SCH_SYMBOL &aSymbol, SPICE_ITEM &aItem, const std::vector< PIN_INFO > &aPins)
const std::list< SPICE_ITEM > & GetItems() const
Return the list of items representing schematic symbols in the Spice world.
void readNodePattern(SPICE_ITEM &aItem)
std::set< wxString > m_rawIncludes
include directives found in symbols
virtual void WriteTail(OUTPUTFORMATTER &aFormatter, unsigned aNetlistOptions)
Write the tail (.end).
NAME_GENERATOR m_modelNameGenerator
Generates unique model names.
bool WriteNetlist(const wxString &aOutFileName, unsigned aNetlistOptions, REPORTER &aReporter) override
Write to specified output file.
An interface used to output 8 bit text in a convenient way.
Definition: richio.h:322
int PRINTF_FUNC Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition: richio.cpp:458
static wxString GetUserCachePath()
Gets the stock (install) 3d viewer plugins path.
Definition: paths.cpp:344
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:71
virtual bool HasMessage() const =0
Returns true if the reporter client is non-empty.
virtual SCH_SHEET_LIST GetSheets() const =0
virtual SCH_SHEET_PATH & CurrentSheet() const =0
virtual PROJECT & Prj() const =0
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:51
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:174
A container for handling SCH_SHEET_PATH objects in a flattened hierarchy.
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
SCH_SHEET * Last() const
Return a pointer to the last SCH_SHEET of the list.
Schematic symbol object.
Definition: sch_symbol.h:105
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly)
Populate a std::vector with SCH_FIELDs.
Definition: sch_symbol.cpp:960
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
Definition: sch_symbol.cpp:713
virtual wxString GetShownText(const SCH_SHEET_PATH *aPath, bool aAllowExtraText, int aDepth=0) const
SIM_MODEL & CreateModel(SIM_MODEL::TYPE aType, const std::vector< SCH_PIN * > &aPins, REPORTER &aReporter)
std::map< wxString, std::reference_wrapper< const SIM_LIBRARY > > GetLibraries() const
const SPICE_GENERATOR & SpiceGenerator() const
Definition: sim_model.h:435
static std::string GetFieldValue(const std::vector< SCH_FIELD > *aFields, const wxString &aFieldName, bool aResolve=true)
Definition: sim_model.cpp:645
std::string IbisDevice(const SPICE_ITEM &aItem, const PROJECT &aProject, const wxString &aCacheDir, REPORTER &aReporter) const
virtual std::string ItemName(const SPICE_ITEM &aItem) const
virtual std::string ModelName(const SPICE_ITEM &aItem) const
bool GetExcludedFromSim() const override
Definition: symbol.h:136
void Clear()
Erase the record.
An 8 bit string that is assuredly encoded in UTF8, and supplies special conversion support to and fro...
Definition: utf8.h:72
const char * c_str() const
Definition: utf8.h:103
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:334
The common library.
void DisplayErrorMessage(wxWindow *aParent, const wxString &aText, const wxString &aExtraInfo)
Display an error message with aMessage.
Definition: confirm.cpp:195
This file is part of the common library.
#define _(s)
wxString ResolveFile(const wxString &aFileName, const ENV_VAR_MAP *aEnvVars, const PROJECT *aProject)
Search the default paths trying to find one with the requested file.
Definition: env_paths.cpp:164
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:39
PROJECT & Prj()
Definition: kicad.cpp:595
must_if< error >::control< Rule > control
PGM_BASE & Pgm()
The global Program "get" accessor.
Definition: pgm_base.cpp:1059
see class PGM_BASE
#define SIM_NODES_FORMAT_FIELD
Definition: sim_model.h:58
std::vector< FAB_LAYER_COLOR > dummy
wxString UnescapeString(const wxString &aSource)
#define TO_UTF8(wxstring)
Convert a wxString to a UTF8 encoded C string for all wxWidgets build modes.
Definition: string_utils.h:391
SIM_MODEL & model
Definition: sim_library.h:41
std::string name
Definition: sim_library.h:40
Notes: spaces are allowed everywhere in any number ~ can only be before ? ~~ is not allowed [] can en...
std::string refName
std::vector< SCH_FIELD > fields
std::string modelName
const SIM_MODEL * model
std::vector< std::string > pinNetNames
std::string baseModelName
std::vector< std::string > pinNumbers
@ REFERENCE_FIELD
Field Reference of part, i.e. "IC21".
@ SCH_SYMBOL_T
Definition: typeinfo.h:172
@ SCH_TEXT_T
Definition: typeinfo.h:151
@ SCH_TEXTBOX_T
Definition: typeinfo.h:152
VECTOR2< int > VECTOR2I
Definition: vector2d.h:602