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 The 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 <common.h>
32#include <confirm.h>
33#include <pgm_base.h>
34#include <env_paths.h>
35#include <richio.h>
38#include <sch_screen.h>
39#include <sch_textbox.h>
40#include <string_utils.h>
41#include <ki_exception.h>
42
44#include <fmt/core.h>
45#include <paths.h>
46#include <wx/dir.h>
47#include <wx/log.h>
48#include <locale_io.h>
49#include "markup_parser.h"
50
51
52std::string NAME_GENERATOR::Generate( const std::string& aProposedName )
53{
54 std::string name = aProposedName;
55 int ii = 1;
56
57 while( m_names.contains( name ) )
58 name = fmt::format( "{}#{}", aProposedName, ii++ );
59
60 return name;
61}
62
63
65 NETLIST_EXPORTER_BASE( aSchematic ),
66 m_libMgr( &aSchematic->Prj() )
67{
68 std::vector<EMBEDDED_FILES*> embeddedFilesStack;
69 embeddedFilesStack.push_back( aSchematic->GetEmbeddedFiles() );
70 m_libMgr.SetFilesStack( std::move( embeddedFilesStack ) );
71}
72
73
74bool NETLIST_EXPORTER_SPICE::WriteNetlist( const wxString& aOutFileName, unsigned aNetlistOptions,
75 REPORTER& aReporter )
76{
77 FILE_OUTPUTFORMATTER formatter( aOutFileName, wxT( "wt" ), '\'' );
78 return DoWriteNetlist( wxEmptyString, aNetlistOptions, formatter, aReporter );
79}
80
81
82bool NETLIST_EXPORTER_SPICE::DoWriteNetlist( const wxString& aSimCommand, unsigned aSimOptions,
83 OUTPUTFORMATTER& aFormatter, REPORTER& aReporter )
84{
86
87 // Cleanup list to avoid duplicate if the netlist exporter is run more than once.
88 m_rawIncludes.clear();
89
90 bool result = ReadSchematicAndLibraries( aSimOptions, aReporter );
91
92 WriteHead( aFormatter, aSimOptions );
93
94 writeIncludes( aFormatter, aSimOptions );
95 writeModels( aFormatter );
96
97 // Skip this if there is no netlist to avoid an ngspice segfault
98 if( !m_items.empty() )
99 WriteDirectives( aSimCommand, aSimOptions, aFormatter );
100
101 writeItems( aFormatter );
102
103 WriteTail( aFormatter, aSimOptions );
104
105 return result;
106}
107
108
109void NETLIST_EXPORTER_SPICE::WriteHead( OUTPUTFORMATTER& aFormatter, unsigned aNetlistOptions )
110{
111 aFormatter.Print( 0, ".title KiCad schematic\n" );
112}
113
114
115void NETLIST_EXPORTER_SPICE::WriteTail( OUTPUTFORMATTER& aFormatter, unsigned aNetlistOptions )
116{
117 aFormatter.Print( 0, ".end\n" );
118}
119
120
122 REPORTER& aReporter )
123{
124 wxString msg;
125 std::set<std::string> refNames; // Set of reference names to check for duplication.
126 int ncCounter = 1;
127
128 ReadDirectives( aNetlistOptions );
129
130 m_nets.clear();
131 m_items.clear();
133 m_libParts.clear();
134
135 wxFileName cacheDir;
136 cacheDir.AssignDir( PATHS::GetUserCachePath() );
137 cacheDir.AppendDir( wxT( "ibis" ) );
138
139 if( !cacheDir.DirExists() )
140 {
141 cacheDir.Mkdir( wxS_DIR_DEFAULT, wxPATH_MKDIR_FULL );
142
143 if( !cacheDir.DirExists() )
144 {
145 wxLogTrace( wxT( "IBIS_CACHE:" ),
146 wxT( "%s:%s:%d\n * failed to create ibis cache directory '%s'" ),
147 __FILE__, __FUNCTION__, __LINE__, cacheDir.GetPath() );
148
149 return false;
150 }
151 }
152
153 wxDir dir;
154 wxString dirName = cacheDir.GetFullPath();
155
156 if( !dir.Open( dirName ) )
157 return false;
158
159 wxFileName thisFile;
160 wxArrayString fileList;
161 wxString fileSpec = wxT( "*.cache" );
162
163 thisFile.SetPath( dirName ); // Set the base path to the cache folder
164
165 size_t numFilesFound = wxDir::GetAllFiles( dirName, &fileList, fileSpec );
166
167 for( size_t ii = 0; ii < numFilesFound; ii++ )
168 {
169 // Completes path to specific file so we can get its "last access" date
170 thisFile.SetFullName( fileList[ii] );
171 wxRemoveFile( thisFile.GetFullPath() );
172 }
173
174 for( SCH_SHEET_PATH& sheet : BuildSheetList( aNetlistOptions ) )
175 {
176 for( SCH_ITEM* item : sheet.LastScreen()->Items().OfType( SCH_SYMBOL_T ) )
177 {
178 SCH_SYMBOL* symbol = findNextSymbol( item, sheet );
179
180 if( !symbol || symbol->ResolveExcludedFromSim() )
181 continue;
182
183 try
184 {
185 SPICE_ITEM spiceItem;
186 std::vector<PIN_INFO> pins = CreatePinList( symbol, sheet, true );
187
188 for( const SCH_FIELD& field : symbol->GetFields() )
189 {
190 spiceItem.fields.emplace_back( symbol, FIELD_T::USER, field.GetName() );
191
192 if( field.GetId() == FIELD_T::REFERENCE )
193 spiceItem.fields.back().SetText( symbol->GetRef( &sheet ) );
194 else
195 spiceItem.fields.back().SetText( field.GetShownText( &sheet, false ) );
196 }
197
198 readRefName( sheet, *symbol, spiceItem, refNames );
199 readModel( sheet, *symbol, spiceItem, aReporter );
200 readPinNumbers( *symbol, spiceItem, pins );
201 readPinNetNames( *symbol, spiceItem, pins, ncCounter );
202 readNodePattern( spiceItem );
203 // TODO: transmission line handling?
204
205 m_items.push_back( std::move( spiceItem ) );
206 }
207 catch( IO_ERROR& e )
208 {
209 aReporter.Report( e.What(), RPT_SEVERITY_ERROR );
210 }
211 }
212 }
213
215}
216
217
219{
220 MARKUP::MARKUP_PARSER markupParser( aNetName->ToStdString() );
221 std::unique_ptr<MARKUP::NODE> root = markupParser.Parse();
222
223 std::function<void( const std::unique_ptr<MARKUP::NODE>&)> convertMarkup =
224 [&]( const std::unique_ptr<MARKUP::NODE>& aNode )
225 {
226 if( aNode )
227 {
228 if( !aNode->is_root() )
229 {
230 if( aNode->isOverbar() )
231 {
232 // ~{CLK} is a different signal than CLK
233 *aNetName += '~';
234 }
235 else if( aNode->isSubscript() || aNode->isSuperscript() )
236 {
237 // V_{OUT} is just a pretty-printed version of VOUT
238 }
239
240 if( aNode->has_content() )
241 *aNetName += aNode->string();
242 }
243
244 for( const std::unique_ptr<MARKUP::NODE>& child : aNode->children )
245 convertMarkup( child );
246 }
247 };
248
249 *aNetName = wxEmptyString;
250 convertMarkup( root );
251
252 // Replace all ngspice-disallowed chars in netnames by a '_'
253 aNetName->Replace( '%', '_' );
254 aNetName->Replace( '(', '_' );
255 aNetName->Replace( ')', '_' );
256 aNetName->Replace( ',', '_' );
257 aNetName->Replace( '[', '_' );
258 aNetName->Replace( ']', '_' );
259 aNetName->Replace( '<', '_' );
260 aNetName->Replace( '>', '_' );
261 aNetName->Replace( '~', '_' );
262 aNetName->Replace( ' ', '_' );
263
264 // A net name on the root sheet with a label '/foo' is going to get titled "//foo". This
265 // will trip up ngspice as "//" opens a line comment.
266 if( aNetName->StartsWith( wxS( "//" ) ) )
267 aNetName->Replace( wxS( "//" ), wxS( "/root/" ), false /* replace all */ );
268}
269
270
271wxString NETLIST_EXPORTER_SPICE::GetItemName( const wxString& aRefName ) const
272{
273 if( const SPICE_ITEM* item = FindItem( aRefName ) )
274 return item->model->SpiceGenerator().ItemName( *item );
275
276 return wxEmptyString;
277}
278
279
280const SPICE_ITEM* NETLIST_EXPORTER_SPICE::FindItem( const wxString& aRefName ) const
281{
282 const std::string refName = aRefName.ToStdString();
283 const std::list<SPICE_ITEM>& spiceItems = GetItems();
284
285 auto it = std::find_if( spiceItems.begin(), spiceItems.end(),
286 [&refName]( const SPICE_ITEM& item )
287 {
288 return item.refName == refName;
289 } );
290
291 if( it != spiceItems.end() )
292 return &*it;
293
294 return nullptr;
295}
296
297
298void NETLIST_EXPORTER_SPICE::ReadDirectives( unsigned aNetlistOptions )
299{
300 wxString msg;
301 wxString text;
302
303 m_directives.clear();
304
305 for( const SCH_SHEET_PATH& sheet : BuildSheetList( aNetlistOptions ) )
306 {
307 for( SCH_ITEM* item : sheet.LastScreen()->Items() )
308 {
309 if( item->ResolveExcludedFromSim() )
310 continue;
311
312 if( item->Type() == SCH_TEXT_T )
313 text = static_cast<SCH_TEXT*>( item )->GetShownText( &sheet, false );
314 else if( item->Type() == SCH_TEXTBOX_T )
315 text = static_cast<SCH_TEXTBOX*>( item )->GetShownText( &sheet, false );
316 else
317 continue;
318
319 // Send anything that contains directives to SPICE
320 wxStringTokenizer tokenizer( text, wxT( "\r\n" ), wxTOKEN_STRTOK );
321 bool foundDirective = false;
322
323 auto isDirective =
324 []( const wxString& line, const wxString& dir )
325 {
326 return line == dir || line.StartsWith( dir + wxS( " " ) );
327 };
328
329 while( tokenizer.HasMoreTokens() )
330 {
331 wxString line = tokenizer.GetNextToken().Upper();
332
333 if( line.StartsWith( wxT( "." ) ) )
334 {
335 if( isDirective( line, wxS( ".AC" ) )
336 || isDirective( line, wxS( ".CONTROL" ) )
337 || isDirective( line, wxS( ".CSPARAM" ) )
338 || isDirective( line, wxS( ".DISTO" ) )
339 || isDirective( line, wxS( ".DC" ) )
340 || isDirective( line, wxS( ".ELSE" ) )
341 || isDirective( line, wxS( ".ELSEIF" ) )
342 || isDirective( line, wxS( ".END" ) )
343 || isDirective( line, wxS( ".ENDC" ) )
344 || isDirective( line, wxS( ".ENDIF" ) )
345 || isDirective( line, wxS( ".ENDS" ) )
346 || isDirective( line, wxS( ".FOUR" ) )
347 || isDirective( line, wxS( ".FUNC" ) )
348 || isDirective( line, wxS( ".GLOBAL" ) )
349 || isDirective( line, wxS( ".IC" ) )
350 || isDirective( line, wxS( ".IF" ) )
351 || isDirective( line, wxS( ".INCLUDE" ) )
352 || isDirective( line, wxS( ".LIB" ) )
353 || isDirective( line, wxS( ".MEAS" ) )
354 || isDirective( line, wxS( ".MODEL" ) )
355 || isDirective( line, wxS( ".NODESET" ) )
356 || isDirective( line, wxS( ".NOISE" ) )
357 || isDirective( line, wxS( ".OP" ) )
358 || isDirective( line, wxS( ".OPTIONS" ) )
359 || isDirective( line, wxS( ".PARAM" ) )
360 || isDirective( line, wxS( ".PLOT" ) )
361 || isDirective( line, wxS( ".PRINT" ) )
362 || isDirective( line, wxS( ".PROBE" ) )
363 || isDirective( line, wxS( ".PZ" ) )
364 || isDirective( line, wxS( ".SAVE" ) )
365 || isDirective( line, wxS( ".SENS" ) )
366 || isDirective( line, wxS( ".SP" ) )
367 || isDirective( line, wxS( ".SUBCKT" ) )
368 || isDirective( line, wxS( ".TEMP" ) )
369 || isDirective( line, wxS( ".TF" ) )
370 || isDirective( line, wxS( ".TITLE" ) )
371 || isDirective( line, wxS( ".TRAN" ) )
372 || isDirective( line, wxS( ".WIDTH" ) ) )
373 {
374 foundDirective = true;
375 break;
376 }
377 }
378 else if( line.StartsWith( wxT( "K" ) ) )
379 {
380 // Check for mutual inductor declaration
381 wxStringTokenizer line_t( line, wxT( " \t" ), wxTOKEN_STRTOK );
382
383 // Coupling ID
384 if( !line_t.HasMoreTokens() || !line_t.GetNextToken().StartsWith( wxT( "K" ) ) )
385 continue;
386
387 // Inductor 1 ID
388 if( !line_t.HasMoreTokens() || !line_t.GetNextToken().StartsWith( wxT( "L" ) ) )
389 continue;
390
391 // Inductor 2 ID
392 if( !line_t.HasMoreTokens() || !line_t.GetNextToken().StartsWith( wxT( "L" ) ) )
393 continue;
394
395 // That's probably distinctive enough not to bother trying to parse the
396 // coupling value. If there's anything else, assume it's the value.
397 if( line_t.HasMoreTokens() )
398 {
399 foundDirective = true;
400 break;
401 }
402 }
403 }
404
405 if( foundDirective )
406 m_directives.emplace_back( text );
407 }
408 }
409}
410
411
413 SPICE_ITEM& aItem, std::set<std::string>& aRefNames )
414{
415 aItem.refName = aSymbol.GetRef( &aSheet );
416
417 if( !aRefNames.insert( aItem.refName ).second )
418 wxASSERT( wxT( "Duplicate refdes encountered; what happened to ReadyToNetlist()?" ) );
419}
420
421
423 SPICE_ITEM& aItem, REPORTER& aReporter )
424{
425 const SIM_LIBRARY::MODEL& libModel = m_libMgr.CreateModel( &aSheet, aSymbol, true, 0, aReporter );
426
427 aItem.baseModelName = libModel.name;
428 aItem.model = &libModel.model;
429
430 std::string modelName = aItem.model->SpiceGenerator().ModelName( aItem );
431 // Resolve model name collisions.
432 aItem.modelName = m_modelNameGenerator.Generate( modelName );
433
434 // FIXME: Don't have special cases for raw Spice models and KIBIS.
435 if( auto rawSpiceModel = dynamic_cast<const SIM_MODEL_RAW_SPICE*>( aItem.model ) )
436 {
437 int libParamIndex = static_cast<int>( SIM_MODEL_RAW_SPICE::SPICE_PARAM::LIB );
438 wxString path = rawSpiceModel->GetParam( libParamIndex ).value;
439
440 if( !path.IsEmpty() )
441 m_rawIncludes.insert( path );
442 }
443 else if( auto ibisModel = dynamic_cast<const SIM_MODEL_IBIS*>( aItem.model ) )
444 {
445 wxFileName cacheFn;
446 cacheFn.AssignDir( PATHS::GetUserCachePath() );
447 cacheFn.AppendDir( wxT( "ibis" ) );
448 cacheFn.SetFullName( aSymbol.GetRef( &aSheet ) + wxT( ".cache" ) );
449
450 wxFile cacheFile( cacheFn.GetFullPath(), wxFile::write );
451
452 if( !cacheFile.IsOpened() )
453 {
454 wxLogError( _( "Could not open file '%s' to write IBIS model" ),
455 cacheFn.GetFullPath() );
456 }
457
458 auto spiceGenerator = static_cast<const SPICE_GENERATOR_IBIS&>( ibisModel->SpiceGenerator() );
459
460 wxString cacheFilepath = cacheFn.GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR );
461 std::string modelData = spiceGenerator.IbisDevice( aItem, m_schematic,
462 cacheFilepath, aReporter );
463
464 cacheFile.Write( wxString( modelData ) );
465 m_rawIncludes.insert( cacheFn.GetFullPath() );
466 }
467}
468
469
471 const std::vector<PIN_INFO>& aPins )
472{
473 for( const PIN_INFO& pin : aPins )
474 aItem.pinNumbers.emplace_back( pin.num.ToStdString() );
475}
476
477
479 const std::vector<PIN_INFO>& aPins, int& aNcCounter )
480{
481 for( const PIN_INFO& pinInfo : aPins )
482 {
483 wxString netName = GenerateItemPinNetName( pinInfo.netName, aNcCounter );
484
485 aItem.pinNetNames.push_back( netName.ToStdString() );
486 m_nets.insert( netName );
487 }
488}
489
490
492 std::vector<std::string>& aModifiers )
493{
494 std::string input = GetFieldValue( &aItem.fields, SIM_NODES_FORMAT_FIELD, true, 0 );
495
496 if( input == "" )
497 return;
498
499 tao::pegtl::string_input<> in( input, "Sim.NodesFormat field" );
500 std::unique_ptr<tao::pegtl::parse_tree::node> root;
501 std::string singleNodeModifier;
502
503 try
504 {
505 root = tao::pegtl::parse_tree::parse<SIM_XSPICE_PARSER_GRAMMAR::nodeSequenceGrammar,
507 tao::pegtl::nothing,
509 for( const auto& node : root->children )
510 {
511 if( node->is_type<SIM_XSPICE_PARSER_GRAMMAR::squareBracketC>() )
512 {
513 //we want ']' to close previous ?
514 aModifiers.back().append( node->string() );
515 }
516 else
517 { //rest goes to the new singleNodeModifier
518 singleNodeModifier.append( node->string() );
519 }
520
521 if( node->is_type<SIM_XSPICE_PARSER_GRAMMAR::nodeName>() )
522 {
523 aModifiers.push_back( singleNodeModifier );
524 singleNodeModifier.erase( singleNodeModifier.begin(), singleNodeModifier.end() );
525 }
526 }
527 }
528 catch( const tao::pegtl::parse_error& e )
529 {
530 THROW_IO_ERROR( wxString::Format( _( "Error in parsing model '%s', error: '%s'" ),
531 aItem.refName, e.what() ) );
532 }
533}
535{
536 std::vector<std::string> xspicePattern;
537 NETLIST_EXPORTER_SPICE::getNodePattern( aItem, xspicePattern );
538
539 if( xspicePattern.empty() )
540 return;
541
542 if( xspicePattern.size() != aItem.pinNetNames.size() )
543 {
544 THROW_IO_ERROR( wxString::Format( _( "Error in parsing model '%s', wrong number of nodes "
545 "'?' in Sim.NodesFormat compared to connections" ),
546 aItem.refName ) );
547 return;
548 }
549
550 auto itNetNames = aItem.pinNetNames.begin();
551
552 for( std::string& pattern : xspicePattern )
553 {
554 // ngspice does not care about aditional spaces, and we make sure that "%d?" is separated
555 const std::string netName = " " + *itNetNames + " ";
556 pattern.replace( pattern.find( "?" ), 1, netName );
557 *itNetNames = pattern;
558 ++itNetNames;
559 }
560}
561
562void NETLIST_EXPORTER_SPICE::writeInclude( OUTPUTFORMATTER& aFormatter, unsigned aNetlistOptions,
563 const wxString& aPath )
564{
565 // First, expand env vars, if any.
566 wxString expandedPath = ExpandEnvVarSubstitutions( aPath, &m_schematic->Prj() );
567
568 // Path may have been authored by someone on a Windows box; convert it to UNIX format
569 expandedPath.Replace( '\\', '/' );
570
571 wxString fullPath;
572
573 if( aNetlistOptions & OPTION_ADJUST_INCLUDE_PATHS )
574 {
575 // Look for the library in known search locations.
576 fullPath = ResolveFile( expandedPath, &Pgm().GetLocalEnvVariables(), &m_schematic->Prj() );
577
578 if( fullPath.IsEmpty() )
579 {
580 wxLogError( _( "Could not find library file '%s'" ), expandedPath );
581 fullPath = expandedPath;
582 }
583 else if( wxFileName::GetPathSeparator() == '\\' )
584 {
585 // Convert it to UNIX format (again) if ResolveFile() returned a Windows style path
586 fullPath.Replace( '\\', '/' );
587 }
588 }
589 else
590 {
591 fullPath = expandedPath;
592 }
593
594 aFormatter.Print( 0, ".include \"%s\"\n", TO_UTF8( fullPath ) );
595}
596
597
598void NETLIST_EXPORTER_SPICE::writeIncludes( OUTPUTFORMATTER& aFormatter, unsigned aNetlistOptions )
599{
600 for( const auto& [path, library] : m_libMgr.GetLibraries() )
601 {
602 if( dynamic_cast<const SIM_LIBRARY_SPICE*>( &library.get() ) )
603 writeInclude( aFormatter, aNetlistOptions, path );
604 }
605
606 for( const wxString& path : m_rawIncludes )
607 writeInclude( aFormatter, aNetlistOptions, path );
608}
609
610
612{
613 for( const SPICE_ITEM& item : m_items )
614 {
615 if( !item.model->IsEnabled() )
616 continue;
617
618 aFormatter.Print( 0, "%s", item.model->SpiceGenerator().ModelLine( item ).c_str() );
619 }
620}
621
622
624{
625 for( const SPICE_ITEM& item : m_items )
626 {
627 if( !item.model->IsEnabled() )
628 continue;
629
630 aFormatter.Print( 0, "%s", item.model->SpiceGenerator().ItemLine( item ).c_str() );
631 }
632}
633
634
635void NETLIST_EXPORTER_SPICE::WriteDirectives( const wxString& aSimCommand, unsigned aSimOptions,
636 OUTPUTFORMATTER& aFormatter ) const
637{
638 if( aSimOptions & OPTION_SAVE_ALL_VOLTAGES )
639 aFormatter.Print( 0, ".save all\n" );
640
641 if( aSimOptions & OPTION_SAVE_ALL_CURRENTS )
642 aFormatter.Print( 0, ".probe alli\n" );
643
644 if( aSimOptions & OPTION_SAVE_ALL_DISSIPATIONS )
645 {
646 for( const SPICE_ITEM& item : m_items )
647 {
648 // ngspice (v39) does not support power measurement for XSPICE devices
649 // XPSICE devices are marked with 'A'
650 std::string itemName = item.model->SpiceGenerator().ItemName( item );
651
652 if( ( item.model->GetPinCount() >= 2 ) && ( itemName.size() > 0 )
653 && ( itemName.c_str()[0] != 'A' ) )
654 {
655 aFormatter.Print( 0, ".probe p(%s)\n", itemName.c_str() );
656 }
657 }
658 }
659
660 auto isSimCommand =
661 []( const wxString& candidate, const wxString& dir )
662 {
663 return candidate == dir || candidate.StartsWith( dir + wxS( " " ) );
664 };
665
666 for( const wxString& directive : m_directives )
667 {
668 bool simCommand = false;
669
670 if( directive.StartsWith( "." ) )
671 {
672 wxString candidate = directive.Upper();
673
674 simCommand = ( isSimCommand( candidate, wxS( ".AC" ) )
675 || isSimCommand( candidate, wxS( ".DC" ) )
676 || isSimCommand( candidate, wxS( ".TRAN" ) )
677 || isSimCommand( candidate, wxS( ".OP" ) )
678 || isSimCommand( candidate, wxS( ".DISTO" ) )
679 || isSimCommand( candidate, wxS( ".NOISE" ) )
680 || isSimCommand( candidate, wxS( ".PZ" ) )
681 || isSimCommand( candidate, wxS( ".SENS" ) )
682 || isSimCommand( candidate, wxS( ".TF" ) ) );
683 }
684
685 if( !simCommand || ( aSimOptions & OPTION_SIM_COMMAND ) )
686 aFormatter.Print( 0, "%s\n", UTF8( directive ).c_str() );
687 }
688}
689
690
691wxString NETLIST_EXPORTER_SPICE::GenerateItemPinNetName( const wxString& aNetName,
692 int& aNcCounter ) const
693{
694 wxString netName = UnescapeString( aNetName );
695
696 ConvertToSpiceMarkup( &netName );
697
698 if( netName.IsEmpty() )
699 netName.Printf( wxS( "NC-%d" ), aNcCounter++ );
700
701 return netName;
702}
703
704
706{
707 SCH_SHEET_LIST sheets;
708
709 if( aNetlistOptions & OPTION_CUR_SHEET_AS_ROOT )
711 else
712 sheets = m_schematic->Hierarchy();
713
714 alg::delete_if( sheets,
715 [&]( const SCH_SHEET_PATH& sheet )
716 {
717 return sheet.GetExcludedFromSim();
718 } );
719
720 return sheets;
721}
722
const char * name
Definition: DXF_plotter.cpp:62
Used for text file output.
Definition: richio.h:491
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:77
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
Instantiate the current locale within a scope in which you are expecting exceptions to be thrown.
Definition: locale_io.h:41
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.
SCHEMATIC * m_schematic
The schematic we're generating a netlist for.
std::vector< PIN_INFO > CreatePinList(SCH_SYMBOL *aSymbol, const SCH_SHEET_PATH &aSheetPath, bool aKeepUnconnectedPins)
Find a symbol from the DrawList and builds its pin list.
SCH_SYMBOL * findNextSymbol(EDA_ITEM *aItem, const SCH_SHEET_PATH &aSheetPath)
Check if the given symbol should be processed for netlisting.
std::set< LIB_SYMBOL *, LIB_SYMBOL_LESS_THAN > m_libParts
unique library symbols used. LIB_SYMBOL items are sorted by names
UNIQUE_STRINGS m_referencesAlreadyFound
Used for "multiple symbols per package" symbols to avoid processing a lib symbol more than once.
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)
std::list< SPICE_ITEM > m_items
void getNodePattern(SPICE_ITEM &aItem, std::vector< std::string > &aModifiers)
static void ConvertToSpiceMarkup(wxString *aNetName)
Remove formatting wrappers and replace illegal spice net name characters with underscores.
void ReadDirectives(unsigned aNetlistOptions)
SCH_SHEET_LIST BuildSheetList(unsigned aNetlistOptions=0) const
Return the paths of exported sheets (either all or the current one).
void readModel(SCH_SHEET_PATH &aSheet, SCH_SYMBOL &aSymbol, SPICE_ITEM &aItem, REPORTER &aReporter)
virtual wxString GenerateItemPinNetName(const wxString &aNetName, int &aNcCounter) const
std::set< wxString > m_nets
Items representing schematic symbols in Spice world.
void readRefName(SCH_SHEET_PATH &aSheet, SCH_SYMBOL &aSymbol, SPICE_ITEM &aItem, std::set< std::string > &aRefNames)
wxString GetItemName(const wxString &aRefName) const
Return name of Spice device corresponding to a schematic symbol.
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).
void writeInclude(OUTPUTFORMATTER &aFormatter, unsigned aNetlistOptions, const wxString &aPath)
virtual void WriteDirectives(const wxString &aSimCommand, unsigned aSimOptions, OUTPUTFORMATTER &candidate) const
NETLIST_EXPORTER_SPICE(SCHEMATIC *aSchematic)
SIM_LIB_MGR m_libMgr
Holds libraries and models.
const SPICE_ITEM * FindItem(const wxString &aRefName) const
Find and return the item corresponding to aRefName.
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.
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_N Print(int nestLevel, const char *fmt,...)
Format and write text to the output stream.
Definition: richio.cpp:463
static wxString GetUserCachePath()
Gets the stock (install) 3d viewer plugins path.
Definition: paths.cpp:424
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:73
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)
Report a string with a given severity.
Definition: reporter.h:102
virtual bool HasMessageOfSeverity(int aSeverityMask) const
Returns true if the reporter has one or more messages matching the specified severity mask.
Definition: reporter.h:140
Holds all the data relating to one schematic.
Definition: schematic.h:87
PROJECT & Prj() const
Return a reference to the project this schematic is part of.
Definition: schematic.h:102
SCH_SHEET_LIST Hierarchy() const
Return the full schematic flattened hierarchical sheet list.
Definition: schematic.cpp:219
EMBEDDED_FILES * GetEmbeddedFiles() override
Definition: schematic.cpp:888
SCH_SHEET_PATH & CurrentSheet() const
Definition: schematic.h:167
Base class for any item which can be embedded within the SCHEMATIC container class,...
Definition: sch_item.h:168
bool ResolveExcludedFromSim() const
Definition: sch_item.cpp:242
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...
bool GetExcludedFromSim() const
SCH_SHEET * Last() const
Return a pointer to the last SCH_SHEET of the list.
Schematic symbol object.
Definition: sch_symbol.h:75
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly) const override
Populate a std::vector with SCH_FIELDs, sorted in ordinal order.
Definition: sch_symbol.cpp:780
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
Definition: sch_symbol.cpp:550
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
void SetFilesStack(std::vector< EMBEDDED_FILES * > aFilesStack)
Definition: sim_lib_mgr.h:49
const SPICE_GENERATOR & SpiceGenerator() const
Definition: sim_model.h:431
std::string IbisDevice(const SPICE_ITEM &aItem, SCHEMATIC *aSchematic, const wxString &aCacheDir, REPORTER &aReporter) const
virtual std::string ModelName(const SPICE_ITEM &aItem) const
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:353
The common library.
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:166
Helper functions to substitute paths with environmental variables.
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:39
PROJECT & Prj()
Definition: kicad.cpp:597
must_if< error >::control< Rule > control
void delete_if(_Container &__c, _Function &&__f)
Deletes all values from __c for which __f returns true.
Definition: kicad_algo.h:174
PGM_BASE & Pgm()
The global program "get" accessor.
Definition: pgm_base.cpp:893
see class PGM_BASE
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_UNDEFINED
wxString GetFieldValue(const std::vector< SCH_FIELD > *aFields, FIELD_T aFieldType)
Definition: sch_field.h:397
#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:429
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
@ SCH_SYMBOL_T
Definition: typeinfo.h:173
@ SCH_TEXT_T
Definition: typeinfo.h:152
@ SCH_TEXTBOX_T
Definition: typeinfo.h:153