KiCad PCB EDA Suite
Loading...
Searching...
No Matches
sim_lib_mgr.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) 2022 Mikolaj Wielgus
5 * Copyright (C) 2022-2023 KiCad Developers, see AUTHORS.txt for contributors.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 3
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, you may find one here:
19 * https://www.gnu.org/licenses/gpl-3.0.html
20 * or you may search the http://www.gnu.org website for the version 3 license,
21 * or you may write to the Free Software Foundation, Inc.,
22 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
23 */
24
25#include <pgm_base.h>
26#include <string>
27#include <string_utils.h>
28#include <common.h>
29#include <functional>
30#include <sch_symbol.h>
31
32// Include simulator headers after wxWidgets headers to avoid conflicts with Windows headers
33// (especially on msys2 + wxWidgets 3.0.x)
34#include <sim/sim_lib_mgr.h>
35#include <sim/sim_library.h>
36#include <sim/sim_model.h>
37#include <sim/sim_model_ideal.h>
38
39using namespace std::placeholders;
40
41
42SIM_LIB_MGR::SIM_LIB_MGR( const PROJECT* aPrj, REPORTER* aReporter ) :
43 m_project( aPrj ),
44 m_reporter( aReporter ),
45 m_forceFullParse( false )
46{
47}
48
49
51{
52 m_libraries.clear();
53 m_models.clear();
54}
55
56
57wxString SIM_LIB_MGR::ResolveLibraryPath( const wxString& aLibraryPath, const PROJECT* aProject )
58{
59 wxString expandedPath = ExpandEnvVarSubstitutions( aLibraryPath, aProject );
60 wxFileName fn( expandedPath );
61
62 if( fn.IsAbsolute() )
63 return fn.GetFullPath();
64
65 wxFileName projectFn( aProject ? aProject->AbsolutePath( expandedPath ) : expandedPath );
66
67 if( projectFn.Exists() )
68 return projectFn.GetFullPath();
69
70 wxFileName spiceLibFn( expandedPath );
71 wxString spiceLibDir;
72
73 wxGetEnv( wxT( "SPICE_LIB_DIR" ), &spiceLibDir );
74
75 if( !spiceLibDir.IsEmpty() && spiceLibFn.MakeAbsolute( spiceLibDir ) && spiceLibFn.Exists() )
76 return spiceLibFn.GetFullPath();
77
78 if( projectFn.GetFullPath() == spiceLibFn.GetFullPath() )
79 {
80 THROW_IO_ERROR( wxString::Format( _( "Simulation model library not found at '%s'" ),
81 spiceLibFn.GetFullPath() ) );
82 }
83 else
84 {
85 THROW_IO_ERROR( wxString::Format( _( "Simulation model library not found at '%s' or '%s'" ),
86 projectFn.GetFullPath(),
87 spiceLibFn.GetFullPath() ) );
88 }
89}
90
91
92wxString SIM_LIB_MGR::ResolveEmbeddedLibraryPath( const wxString& aLibPath,
93 const wxString& aRelativeLib )
94{
95 wxFileName testPath( aLibPath );
96 wxString fullPath( aLibPath );
97
98 if( !testPath.IsAbsolute() && !aRelativeLib.empty() )
99 {
100 wxString relLib( aRelativeLib );
101
102 try
103 {
104 relLib = ResolveLibraryPath( relLib, m_project );
105 }
106 catch( ... )
107 {}
108
109 wxFileName fn( relLib );
110
111 testPath.MakeAbsolute( fn.GetPath( true ) );
112 fullPath = testPath.GetFullPath();
113 }
114
115 try
116 {
117 wxFileName fn( fullPath );
118
119 if( !fn.Exists() )
120 fullPath = aLibPath;
121
122 fullPath = ResolveLibraryPath( fullPath, m_project );
123 }
124 catch( ... )
125 {}
126
127 return fullPath;
128}
129
130
131void SIM_LIB_MGR::SetLibrary( const wxString& aLibraryPath )
132{
133 try
134 {
135 wxString path = ResolveLibraryPath( aLibraryPath, m_project );
136
137 std::function<wxString(const wxString&, const wxString&)> f2 =
138 std::bind( &SIM_LIB_MGR::ResolveEmbeddedLibraryPath, this, _1, _2 );
139
140 std::unique_ptr<SIM_LIBRARY> library = SIM_LIBRARY::Create( path, m_forceFullParse,
141 m_reporter, &f2 );
142
143 Clear();
144 m_libraries[path] = std::move( library );
145 }
146 catch( const IO_ERROR& e )
147 {
148 m_reporter->Report( e.What() );
149 }
150}
151
152
153SIM_MODEL& SIM_LIB_MGR::CreateModel( SIM_MODEL::TYPE aType, const std::vector<LIB_PIN*>& aPins )
154{
155 m_models.push_back( SIM_MODEL::Create( aType, aPins, m_reporter ) );
156 return *m_models.back();
157}
158
159
161 const std::vector<LIB_PIN*>& aPins )
162{
163 m_models.push_back( SIM_MODEL::Create( aBaseModel, aPins, m_reporter ) );
164 return *m_models.back();
165}
166
167
168template <typename T>
170 const std::vector<LIB_PIN*>& aPins,
171 const std::vector<T>& aFields )
172{
173 m_models.push_back( SIM_MODEL::Create( aBaseModel, aPins, aFields, m_reporter ) );
174 return *m_models.back();
175}
176
177template SIM_MODEL& SIM_LIB_MGR::CreateModel( const SIM_MODEL* aBaseModel,
178 const std::vector<LIB_PIN*>& aPins,
179 const std::vector<SCH_FIELD>& aFields );
180template SIM_MODEL& SIM_LIB_MGR::CreateModel( const SIM_MODEL* aBaseModel,
181 const std::vector<LIB_PIN*>& aPins,
182 const std::vector<LIB_FIELD>& aFields );
183
184
186{
187 // Note: currently this creates a resolved model (all Kicad variables references are resolved
188 // before building the model).
189 //
190 // That's not what we want if this is ever called from the Simulation Model Editor (or other
191 // editors, but it is what we want if called to generate a netlist or other exported items.
192
193
194 std::vector<SCH_FIELD> fields;
195
196 for( const SCH_FIELD& field : aSymbol.GetFields() )
197 {
198 fields.emplace_back( VECTOR2I(), -1, &aSymbol, field.GetName() );
199
200 if( field.GetId() == REFERENCE_FIELD )
201 fields.back().SetText( aSymbol.GetRef( aSheetPath ) );
202 else
203 fields.back().SetText( field.GetShownText( aSheetPath, false ) );
204 }
205
206 wxString deviceType;
207 wxString modelType;
208 wxString modelParams;
209 wxString pinMap;
210 bool storeInValue = false;
211
212 // Infer RLC and VI models if they aren't specified
214 &deviceType, &modelType, &modelParams, &pinMap ) )
215 {
216 fields.emplace_back( &aSymbol, -1, SIM_DEVICE_TYPE_FIELD );
217 fields.back().SetText( deviceType );
218
219 if( !modelType.IsEmpty() )
220 {
221 fields.emplace_back( &aSymbol, -1, SIM_TYPE_FIELD );
222 fields.back().SetText( modelType );
223 }
224
225 fields.emplace_back( &aSymbol, -1, SIM_PARAMS_FIELD );
226 fields.back().SetText( modelParams );
227
228 fields.emplace_back( &aSymbol, -1, SIM_PINS_FIELD );
229 fields.back().SetText( pinMap );
230
231 storeInValue = true;
232 }
233
234 std::vector<LIB_PIN*> sourcePins = aSymbol.GetAllLibPins();
235
236 std::sort( sourcePins.begin(), sourcePins.end(),
237 []( const LIB_PIN* lhs, const LIB_PIN* rhs )
238 {
239 return StrNumCmp( lhs->GetNumber(), rhs->GetNumber(), true ) < 0;
240 } );
241
242 SIM_LIBRARY::MODEL model = CreateModel( fields, sourcePins, true );
243
244 model.model.SetIsStoredInValue( storeInValue );
245
246 return model;
247}
248
249
250template <typename T>
251SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const std::vector<T>& aFields,
252 const std::vector<LIB_PIN*>& aPins, bool aResolved )
253{
254 std::string libraryPath = SIM_MODEL::GetFieldValue( &aFields, SIM_LIBRARY::LIBRARY_FIELD );
255 std::string baseModelName = SIM_MODEL::GetFieldValue( &aFields, SIM_LIBRARY::NAME_FIELD );
256
257 if( libraryPath != "" )
258 {
259 return CreateModel( libraryPath, baseModelName, aFields, aPins );
260 }
261 else
262 {
263 m_models.push_back( SIM_MODEL::Create( aFields, aPins, aResolved, m_reporter ) );
264 return { baseModelName, *m_models.back() };
265 }
266}
267
268template SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const std::vector<SCH_FIELD>& aFields,
269 const std::vector<LIB_PIN*>& aPins,
270 bool aResolved );
271template SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const std::vector<LIB_FIELD>& aFields,
272 const std::vector<LIB_PIN*>& aPins,
273 bool aResolved );
274
275
276template <typename T>
277SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const wxString& aLibraryPath,
278 const std::string& aBaseModelName,
279 const std::vector<T>& aFields,
280 const std::vector<LIB_PIN*>& aPins )
281{
282 wxString path;
283 wxString msg;
284 SIM_LIBRARY* library = nullptr;
285 SIM_MODEL* baseModel = nullptr;
286 std::string modelName;
287
288 try
289 {
290 path = ResolveLibraryPath( aLibraryPath, m_project );
291
292 auto it = m_libraries.find( path );
293
294 if( it == m_libraries.end() )
295 {
296 std::function<wxString( const wxString&, const wxString& )> f2 =
297 std::bind( &SIM_LIB_MGR::ResolveEmbeddedLibraryPath, this, _1, _2 );
298
300 m_reporter, &f2 ) ).first;
301 }
302
303 library = &*it->second;
304 }
305 catch( const IO_ERROR& e )
306 {
307 if( m_reporter )
308 {
309 msg.Printf( _( "Error loading simulation model library '%s': %s" ),
310 path,
311 e.What() );
312
314 }
315 }
316
317 if( aBaseModelName == "" )
318 {
319 if( m_reporter )
320 {
321 msg.Printf( _( "Error loading simulation model: no '%s' field" ),
323
325 }
326
327 modelName = _( "unknown" ).ToStdString();
328 }
329 else if( library )
330 {
331 baseModel = library->FindModel( aBaseModelName );
332 modelName = aBaseModelName;
333
334 if( !baseModel )
335 {
336 if( m_reporter )
337 {
338 msg.Printf( _( "Error loading simulation model: could not find base model '%s' "
339 "in library '%s'" ),
340 aBaseModelName,
341 path );
342
344 }
345 }
346 }
347
348 m_models.push_back( SIM_MODEL::Create( baseModel, aPins, aFields, m_reporter ) );
349
350 return { modelName, *m_models.back() };
351}
352
353
354void SIM_LIB_MGR::SetModel( int aIndex, std::unique_ptr<SIM_MODEL> aModel )
355{
356 m_models.at( aIndex ) = std::move( aModel );
357}
358
359
360std::map<wxString, std::reference_wrapper<const SIM_LIBRARY>> SIM_LIB_MGR::GetLibraries() const
361{
362 std::map<wxString, std::reference_wrapper<const SIM_LIBRARY>> libraries;
363
364 for( auto& [path, library] : m_libraries )
365 libraries.try_emplace( path, *library );
366
367 return libraries;
368}
369
370
371std::vector<std::reference_wrapper<SIM_MODEL>> SIM_LIB_MGR::GetModels() const
372{
373 std::vector<std::reference_wrapper<SIM_MODEL>> models;
374
375 for( const std::unique_ptr<SIM_MODEL>& model : m_models )
376 models.emplace_back( *model );
377
378 return models;
379}
Hold an error message and may be used when throwing exceptions containing meaningful error messages.
Definition: ki_exception.h:76
virtual const wxString What() const
A composite of Problem() and Where()
Definition: exceptions.cpp:30
Container for project specific data.
Definition: project.h:64
virtual const wxString AbsolutePath(const wxString &aFileName) const
Fix up aFileName if it is relative to the project's directory to be an absolute path and filename.
Definition: project.cpp:305
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:71
virtual REPORTER & Report(const wxString &aText, SEVERITY aSeverity=RPT_SEVERITY_UNDEFINED)=0
Report a string with a given severity.
Instances are attached to a symbol or sheet and provide a place for the symbol's value,...
Definition: sch_field.h:51
Handle access to a stack of flattened SCH_SHEET objects by way of a path for creating a flattened sch...
Schematic symbol object.
Definition: sch_symbol.h:81
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const
Return the reference for the given sheet path.
Definition: sch_symbol.cpp:698
std::vector< LIB_PIN * > GetAllLibPins() const
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly)
Populate a std::vector with SCH_FIELDs.
Definition: sch_symbol.cpp:939
static std::unique_ptr< SIM_LIBRARY > Create(const wxString &aFilePath, bool aForceFullParse, REPORTER *aReporter, std::function< wxString(const wxString &, const wxString &)> *aResolver)
Read library from a source file (e.g.
Definition: sim_library.cpp:33
static constexpr auto LIBRARY_FIELD
Definition: sim_library.h:35
static constexpr auto NAME_FIELD
Definition: sim_library.h:36
void SetModel(int aIndex, std::unique_ptr< SIM_MODEL > aModel)
wxString ResolveEmbeddedLibraryPath(const wxString &aLibPath, const wxString &aRelativeLib)
Definition: sim_lib_mgr.cpp:92
const PROJECT * m_project
Definition: sim_lib_mgr.h:83
void SetLibrary(const wxString &aLibraryPath)
SIM_LIB_MGR(const PROJECT *aPrj, REPORTER *aReporter=nullptr)
Definition: sim_lib_mgr.cpp:42
bool m_forceFullParse
Definition: sim_lib_mgr.h:85
std::vector< std::unique_ptr< SIM_MODEL > > m_models
Definition: sim_lib_mgr.h:87
static wxString ResolveLibraryPath(const wxString &aLibraryPath, const PROJECT *aProject)
Definition: sim_lib_mgr.cpp:57
std::map< wxString, std::reference_wrapper< const SIM_LIBRARY > > GetLibraries() const
std::vector< std::reference_wrapper< SIM_MODEL > > GetModels() const
SIM_MODEL & CreateModel(SIM_MODEL::TYPE aType, const std::vector< LIB_PIN * > &aPins)
REPORTER * m_reporter
Definition: sim_lib_mgr.h:84
void Clear()
Definition: sim_lib_mgr.cpp:50
std::map< wxString, std::unique_ptr< SIM_LIBRARY > > m_libraries
Definition: sim_lib_mgr.h:86
static std::unique_ptr< SIM_MODEL > Create(TYPE aType, const std::vector< LIB_PIN * > &aPins, REPORTER *aReporter)
Definition: sim_model.cpp:451
static bool InferSimModel(T_symbol &aSymbol, std::vector< T_field > *aFields, bool aResolve, SIM_VALUE_GRAMMAR::NOTATION aNotation, wxString *aDeviceType, wxString *aModelType, wxString *aModelParams, wxString *aPinMap)
Definition: sim_model.cpp:1112
static std::string GetFieldValue(const std::vector< T > *aFields, const wxString &aFieldName, bool aResolve=true)
Definition: sim_model.cpp:629
void SetIsStoredInValue(bool aIsStoredInValue)
Definition: sim_model.h:502
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:299
The common library.
#define _(s)
#define THROW_IO_ERROR(msg)
Definition: ki_exception.h:38
see class PGM_BASE
@ RPT_SEVERITY_ERROR
#define SIM_PINS_FIELD
Definition: sim_model.h:54
#define SIM_DEVICE_TYPE_FIELD
Definition: sim_model.h:52
#define SIM_TYPE_FIELD
Definition: sim_model.h:53
#define SIM_PARAMS_FIELD
Definition: sim_model.h:55
SIM_MODEL & model
Definition: sim_library.h:41
@ REFERENCE_FIELD
Field Reference of part, i.e. "IC21".
VECTOR2< int > VECTOR2I
Definition: vector2d.h:588