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-2024 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
43 m_project( aPrj ),
44 m_forceFullParse( false )
45{
46}
47
48
50{
51 m_libraries.clear();
52 m_models.clear();
53}
54
55
56wxString SIM_LIB_MGR::ResolveLibraryPath( const wxString& aLibraryPath, const PROJECT* aProject,
57 REPORTER& aReporter )
58{
59 wxString expandedPath = ExpandEnvVarSubstitutions( aLibraryPath, aProject );
60
61 // Convert it to UNIX format
62 expandedPath.Replace( '\\', '/' );
63
64 wxFileName fn( expandedPath );
65
66 if( fn.IsAbsolute() )
67 return fn.GetFullPath();
68
69 wxFileName projectFn( aProject ? aProject->AbsolutePath( expandedPath ) : expandedPath );
70
71 if( projectFn.Exists() )
72 return projectFn.GetFullPath();
73
74 wxFileName spiceLibFn( expandedPath );
75 wxString spiceLibDir;
76
77 wxGetEnv( wxT( "SPICE_LIB_DIR" ), &spiceLibDir );
78
79 if( !spiceLibDir.IsEmpty() && spiceLibFn.MakeAbsolute( spiceLibDir ) && spiceLibFn.Exists() )
80 return spiceLibFn.GetFullPath();
81
82 if( spiceLibDir.IsEmpty() || spiceLibFn.GetFullPath() == projectFn.GetFullPath() )
83 {
84 aReporter.Report( wxString::Format( _( "Simulation model library not found at '%s'" ),
85 projectFn.GetFullPath() ) );
86 }
87 else
88 {
89 aReporter.Report( wxString::Format( _( "Simulation model library not found at '%s' or '%s'" ),
90 projectFn.GetFullPath(),
91 spiceLibFn.GetFullPath() ) );
92 }
93
94 return aLibraryPath;
95}
96
97
98wxString SIM_LIB_MGR::ResolveEmbeddedLibraryPath( const wxString& aLibPath,
99 const wxString& aRelativeLib,
100 REPORTER& aReporter )
101{
102 wxFileName testPath( aLibPath );
103 wxString fullPath( aLibPath );
104
105 if( !testPath.IsAbsolute() && !aRelativeLib.empty() )
106 {
107 wxString relLib( aRelativeLib );
108
109 relLib = ResolveLibraryPath( relLib, m_project, aReporter );
110
111 wxFileName fn( relLib );
112
113 testPath.MakeAbsolute( fn.GetPath( true ) );
114 fullPath = testPath.GetFullPath();
115 }
116
117 wxFileName fn( fullPath );
118
119 if( !fn.Exists() )
120 fullPath = aLibPath;
121
122 fullPath = ResolveLibraryPath( fullPath, m_project, aReporter );
123
124 return fullPath;
125}
126
127
128void SIM_LIB_MGR::SetLibrary( const wxString& aLibraryPath, REPORTER& aReporter )
129{
130 wxString path = ResolveLibraryPath( aLibraryPath, m_project, aReporter );
131
133 return;
134
135 if( !wxFileName::Exists( path ) )
136 {
137 aReporter.Report( wxString::Format( _( "Simulation model library not found at '%s'" ),
138 path ) );
139 return;
140 }
141
142 std::unique_ptr<SIM_LIBRARY> library = SIM_LIBRARY::Create( path, m_forceFullParse, aReporter,
143 [&]( const wxString& libPath, const wxString& relativeLib ) -> wxString
144 {
145 return ResolveEmbeddedLibraryPath( libPath, relativeLib, aReporter );
146 } );
147
148 Clear();
149 m_libraries[path] = std::move( library );
150}
151
152
153SIM_MODEL& SIM_LIB_MGR::CreateModel( SIM_MODEL::TYPE aType, const std::vector<SCH_PIN*>& aPins,
154 REPORTER& aReporter )
155{
156 m_models.push_back( SIM_MODEL::Create( aType, aPins, aReporter ) );
157 return *m_models.back();
158}
159
160
162 const std::vector<SCH_PIN*>& aPins, REPORTER& aReporter )
163{
164 m_models.push_back( SIM_MODEL::Create( aBaseModel, aPins, aReporter ) );
165 return *m_models.back();
166}
167
168
170 const std::vector<SCH_PIN*>& aPins,
171 const std::vector<SCH_FIELD>& aFields, REPORTER& aReporter )
172{
173 m_models.push_back( SIM_MODEL::Create( aBaseModel, aPins, aFields, aReporter ) );
174 return *m_models.back();
175}
176
178 REPORTER& aReporter )
179{
180 // Note: currently this creates a resolved model (all Kicad variables references are resolved
181 // before building the model).
182 //
183 // That's not what we want if this is ever called from the Simulation Model Editor (or other
184 // editors, but it is what we want if called to generate a netlist or other exported items.
185
186
187 std::vector<SCH_FIELD> fields;
188
189 for( const SCH_FIELD& field : aSymbol.GetFields() )
190 {
191 if( field.GetId() == REFERENCE_FIELD )
192 {
193 fields.emplace_back( VECTOR2I(), -1, &aSymbol, field.GetName() );
194 fields.back().SetText( aSymbol.GetRef( aSheetPath ) );
195 }
196 else if( field.GetId() == VALUE_FIELD
197 || field.GetName().StartsWith( wxS( "Sim." ) ) )
198 {
199 fields.emplace_back( VECTOR2I(), -1, &aSymbol, field.GetName() );
200 fields.back().SetText( field.GetShownText( aSheetPath, false ) );
201 }
202 }
203
204 auto getOrCreateField =
205 [&aSymbol, &fields]( const wxString& name ) -> SCH_FIELD*
206 {
207 for( SCH_FIELD& field : fields )
208 {
209 if( field.GetName().IsSameAs( name ) )
210 return &field;
211 }
212
213 fields.emplace_back( &aSymbol, -1, name );
214 return &fields.back();
215 };
216
217 wxString deviceType;
218 wxString modelType;
219 wxString modelParams;
220 wxString pinMap;
221 bool storeInValue = false;
222
223 // Infer RLC and VI models if they aren't specified
225 &deviceType, &modelType, &modelParams, &pinMap ) )
226 {
227 getOrCreateField( SIM_DEVICE_FIELD )->SetText( deviceType );
228
229 if( !modelType.IsEmpty() )
230 getOrCreateField( SIM_DEVICE_SUBTYPE_FIELD )->SetText( modelType );
231
232 getOrCreateField( SIM_PARAMS_FIELD )->SetText( modelParams );
233 getOrCreateField( SIM_PINS_FIELD )->SetText( pinMap );
234
235 storeInValue = true;
236 }
237
238 std::vector<SCH_PIN*> sourcePins = aSymbol.GetAllLibPins();
239
240 std::sort( sourcePins.begin(), sourcePins.end(),
241 []( const SCH_PIN* lhs, const SCH_PIN* rhs )
242 {
243 return StrNumCmp( lhs->GetNumber(), rhs->GetNumber(), true ) < 0;
244 } );
245
246 SIM_LIBRARY::MODEL model = CreateModel( fields, sourcePins, true, aReporter );
247
248 model.model.SetIsStoredInValue( storeInValue );
249
250 return model;
251}
252
253
254SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const std::vector<SCH_FIELD>& aFields,
255 const std::vector<SCH_PIN*>& aPins, bool aResolved,
256 REPORTER& aReporter )
257{
258 std::string libraryPath = SIM_MODEL::GetFieldValue( &aFields, SIM_LIBRARY::LIBRARY_FIELD );
259 std::string baseModelName = SIM_MODEL::GetFieldValue( &aFields, SIM_LIBRARY::NAME_FIELD );
260
261 if( libraryPath != "" )
262 {
263 return CreateModel( libraryPath, baseModelName, aFields, aPins, aReporter );
264 }
265 else
266 {
267 m_models.push_back( SIM_MODEL::Create( aFields, aPins, aResolved, aReporter ) );
268 return { baseModelName, *m_models.back() };
269 }
270}
271
272
273SIM_LIBRARY::MODEL SIM_LIB_MGR::CreateModel( const wxString& aLibraryPath,
274 const std::string& aBaseModelName,
275 const std::vector<SCH_FIELD>& aFields,
276 const std::vector<SCH_PIN*>& aPins,
277 REPORTER& aReporter )
278{
279 wxString msg;
280 SIM_LIBRARY* library = nullptr;
281 SIM_MODEL* baseModel = nullptr;
282 std::string modelName;
283 wxString path = ResolveLibraryPath( aLibraryPath, m_project, aReporter );
284
285 auto it = m_libraries.find( path );
286
287 if( it == m_libraries.end() )
288 {
289 it = m_libraries.emplace( path, SIM_LIBRARY::Create( path, m_forceFullParse, aReporter,
290 [&]( const wxString& libPath, const wxString& relativeLib ) -> wxString
291 {
292 return ResolveEmbeddedLibraryPath( libPath, relativeLib, aReporter );
293 } ) ).first;
294 }
295
296 library = &*it->second;
297
298 if( aBaseModelName == "" )
299 {
300 msg.Printf( _( "Error loading simulation model: no '%s' field" ),
302
303 aReporter.Report( msg, RPT_SEVERITY_ERROR );
304
305 modelName = _( "unknown" ).ToStdString();
306 }
307 else if( library )
308 {
309 baseModel = library->FindModel( aBaseModelName );
310 modelName = aBaseModelName;
311
312 if( !baseModel )
313 {
314 msg.Printf( _( "Error loading simulation model: could not find base model '%s' "
315 "in library '%s'" ),
316 aBaseModelName,
317 path );
318
319 aReporter.Report( msg, RPT_SEVERITY_ERROR );
320 }
321 }
322
323 m_models.push_back( SIM_MODEL::Create( baseModel, aPins, aFields, aReporter ) );
324
325 return { modelName, *m_models.back() };
326}
327
328
329void SIM_LIB_MGR::SetModel( int aIndex, std::unique_ptr<SIM_MODEL> aModel )
330{
331 m_models.at( aIndex ) = std::move( aModel );
332}
333
334
335std::map<wxString, std::reference_wrapper<const SIM_LIBRARY>> SIM_LIB_MGR::GetLibraries() const
336{
337 std::map<wxString, std::reference_wrapper<const SIM_LIBRARY>> libraries;
338
339 for( auto& [path, library] : m_libraries )
340 libraries.try_emplace( path, *library );
341
342 return libraries;
343}
344
345
346std::vector<std::reference_wrapper<SIM_MODEL>> SIM_LIB_MGR::GetModels() const
347{
348 std::vector<std::reference_wrapper<SIM_MODEL>> models;
349
350 for( const std::unique_ptr<SIM_MODEL>& model : m_models )
351 models.emplace_back( *model );
352
353 return models;
354}
const char * name
Definition: DXF_plotter.cpp:57
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:359
A pure virtual class used to derive REPORTER objects from.
Definition: reporter.h:72
virtual bool HasMessageOfSeverity(int aSeverityMask) const
Returns true if the reporter has one or more messages matching the specified severity mask.
Definition: reporter.cpp:53
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
void SetText(const wxString &aText) override
Definition: sch_field.cpp:1212
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:104
std::vector< SCH_PIN * > GetAllLibPins() const
void GetFields(std::vector< SCH_FIELD * > &aVector, bool aVisibleOnly)
Populate a std::vector with SCH_FIELDs.
Definition: sch_symbol.cpp:987
const wxString GetRef(const SCH_SHEET_PATH *aSheet, bool aIncludeUnit=false) const override
Definition: sch_symbol.cpp:737
static std::unique_ptr< SIM_LIBRARY > Create(const wxString &aFilePath, bool aForceFullParse, REPORTER &aReporter, const 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)
const PROJECT * m_project
Definition: sim_lib_mgr.h:83
bool m_forceFullParse
Definition: sim_lib_mgr.h:84
SIM_MODEL & CreateModel(SIM_MODEL::TYPE aType, const std::vector< SCH_PIN * > &aPins, REPORTER &aReporter)
SIM_LIB_MGR(const PROJECT *aPrj)
Definition: sim_lib_mgr.cpp:42
std::vector< std::unique_ptr< SIM_MODEL > > m_models
Definition: sim_lib_mgr.h:86
static wxString ResolveLibraryPath(const wxString &aLibraryPath, const PROJECT *aProject, REPORTER &aReporter)
Definition: sim_lib_mgr.cpp:56
std::map< wxString, std::reference_wrapper< const SIM_LIBRARY > > GetLibraries() const
wxString ResolveEmbeddedLibraryPath(const wxString &aLibPath, const wxString &aRelativeLib, REPORTER &aReporter)
Definition: sim_lib_mgr.cpp:98
std::vector< std::reference_wrapper< SIM_MODEL > > GetModels() const
void Clear()
Definition: sim_lib_mgr.cpp:49
void SetLibrary(const wxString &aLibraryPath, REPORTER &aReporter)
std::map< wxString, std::unique_ptr< SIM_LIBRARY > > m_libraries
Definition: sim_lib_mgr.h:85
static bool InferSimModel(T &aSymbol, std::vector< SCH_FIELD > *aFields, bool aResolve, SIM_VALUE_GRAMMAR::NOTATION aNotation, wxString *aDeviceType, wxString *aModelType, wxString *aModelParams, wxString *aPinMap)
Definition: sim_model.cpp:1082
static std::string GetFieldValue(const std::vector< SCH_FIELD > *aFields, const wxString &aFieldName, bool aResolve=true)
Definition: sim_model.cpp:654
static std::unique_ptr< SIM_MODEL > Create(TYPE aType, const std::vector< SCH_PIN * > &aPins, REPORTER &aReporter)
Definition: sim_model.cpp:496
void SetIsStoredInValue(bool aIsStoredInValue)
Definition: sim_model.h:504
const wxString ExpandEnvVarSubstitutions(const wxString &aString, const PROJECT *aProject)
Replace any environment variable & text variable references with their values.
Definition: common.cpp:348
The common library.
#define _(s)
see class PGM_BASE
@ RPT_SEVERITY_ERROR
@ RPT_SEVERITY_UNDEFINED
#define SIM_PINS_FIELD
Definition: sim_model.h:54
#define SIM_DEVICE_FIELD
Definition: sim_model.h:52
#define SIM_PARAMS_FIELD
Definition: sim_model.h:55
#define SIM_DEVICE_SUBTYPE_FIELD
Definition: sim_model.h:53
SIM_MODEL & model
Definition: sim_library.h:41
@ VALUE_FIELD
Field Value of part, i.e. "3.3K".
@ REFERENCE_FIELD
Field Reference of part, i.e. "IC21".
VECTOR2< int32_t > VECTOR2I
Definition: vector2d.h:691