JUK1
Simulator.hpp
Go to the documentation of this file.
1 #ifndef _SIMULATOR_HPP_INC_
2 #define _SIMULATOR_HPP_INC_
3 
4 #ifdef WITH_PYTHON
5 #include "matplotlibcpp.h"
6 #endif
7 
10 
11 #ifdef WITH_MATLAB
12 #include "MatlabEngine.hpp"
13 #include "MatlabDataArray.hpp"
14 #endif
15 
16 
17 #include <string>
18 #include <regex>
19 #include <iostream>
20 #include <fstream>
21 
23 enum class LineType {
24  Resistor = 'R',
25  Capacitor = 'C',
26  Inductor = 'L',
27  CurrentSource = 'I',
28  VoltageSource = 'V',
29  SParameterBlock = 'S',
30  Transistor = 'Q',
31  Diode = 'D',
32  Comment = '%',
33  Directive = '.',
34 };
35 
37 enum class SourceType {
38  TimeSeries = 'T',
39  Sinusoidal = 'S',
40 };
41 
45 template<typename VT>
47  public:
54  solutionMat(0, 0) {
55 #ifdef WITH_MATLAB
56  matlabDesktop = matlab::engine::findMATLAB().size() > 0;
57  matlabEngine = matlab::engine::connectMATLAB();
58  elements.matlabEngine = matlabEngine;
59 #endif
60  std::ifstream netlist(netlistPath);
61 
62  std::string line;
63 
64  std::regex transientRegex(R"(^\.transient\‍((.+?),(.+?),(.+?)\)\s?$)");
65  std::regex graphRegex(R"(^\.graph\‍((.+?)\)\s?$)");
66  std::regex noDCRegex(R"(^\.nodc\s?$)");
67  std::regex outputFileRegex(R"(^\.outputFile\‍(\s*['"](.+?)['"]\s*\)\s?$)");
68  std::smatch matches;
69 
70  while (!netlist.eof()) {
71  std::getline(netlist, line);
72 
73  LineType lType = static_cast<LineType>(line[0]);
74 
75  switch (lType) {
76  case LineType::Resistor:
79  break;
81  if (line[1] == 'N') {
84  } else {
87  }
88  break;
89  case LineType::Inductor:
92  break;
94  if (line[1] == 'N') {
98  } else {
101  }
102  break;
104  switch (static_cast<SourceType>(line[1])) {
107  VT>::addToElements(line, elements, numNodes,
109  break;
112  VT>::addToElements(line, elements, numNodes,
114  break;
115  default:
118  numDCCurrents);
119  break;
120  }
121  break;
123  if (line[1] == 'V' && (line[2] == 'P' || line[2] == 'F')) {
124 #ifndef WITH_MATLAB
125  if (line[2] == 'F') {
126  std::throw(
127  "ERROR: Matlab not available at compile time");
128  }
129 #endif
132  numDCCurrents);
133  } else {
135  numCurrents,
136  numDCCurrents);
137  }
138  break;
140  if (line[1] == 'N') {
143  } else if (line[1] == 'P') {
146  } else if (line[1] == 'M') {
147  if (line[2] == 'N') {
150  } else {
151  std::cout << "Other Transistors not implemented yet"
152  << std::endl;
153  }
154  } else {
155  std::cout << "Other Transistors not implemented yet"
156  << std::endl;
157  }
158  break;
159  case LineType::Diode:
161  numDCCurrents);
162  break;
163  case LineType::Comment:
164  break;
165  case LineType::Directive:
166  std::regex_match(line, matches, transientRegex);
167 
168  if (matches.size()) {
169  if constexpr (std::is_same_v<VT, double> ||
170  std::is_same_v<VT, float>) {
171  initialTime = std::stod(matches.str(1));
172  timestep = std::stod(matches.str(3));
173  finalTime = std::stod(matches.str(2));
175  } else {
176  static_assert("Unsupported Type");
177  }
178  break;
179  }
180 
181  std::regex_match(line, matches, graphRegex);
182  if (matches.size()) {
183  parseGraph(matches.str(1));
184  break;
185  }
186 
187  std::regex_match(line, matches, noDCRegex);
188  if (matches.size()) {
189  performDCAnalysis = false;
190  break;
191  }
192 
193  std::regex_match(line, matches, outputFileRegex);
194  if (matches.size()) {
195  outputFilePath = matches.str(1);
196  break;
197  }
198 
199  std::cout << "Unsupported Directive" << std::endl;
200 
201  break;
202  }
203  }
204 
206 
207  size_t sizeMat = elements.staticStamp.G.M;
208 
209  solutionMat = Matrix<VT>(sizeMat, steps, 0);
210 
211  luPair = LUPair<VT>(sizeMat);
212  scratchSpace = Matrix<VT>(sizeMat, 1);
213 
214  for (auto & comp : elements.staticElements) {
215  comp->setTimestep(timestep);
216  }
217  for (auto & comp : elements.dynamicElements) {
218  comp->setTimestep(timestep);
219  }
220  for (auto & comp : elements.nonLinearElements) {
221  comp->setTimestep(timestep);
222  }
223 
224  if (performDCAnalysis) {
225  setDCOpPoint();
226  }
227  }
228 
230  void setDCOpPoint() {
234 
235  auto simStartTime = std::chrono::high_resolution_clock::now();
236  for (size_t nr = 0; nr < 35; nr++) {
237  auto & stamp = elements.generateDCStamp(dcSoln, numCurrents);
238  stamp.G.luPair(luPair);
239  stamp.G.leftDivide(stamp.s, luPair, scratchSpace, dcSoln);
240  }
241 
242  for (size_t k = 0; k < solutionMat.M; k++) {
243  solutionMat(k, 0) = dcSoln(k, 0);
244  }
245 
247 
248  auto simEndTime = std::chrono::high_resolution_clock::now();
249  auto timeTaken = std::chrono::duration_cast<std::chrono::nanoseconds>(
250  simEndTime - simStartTime)
251  .count();
252 
253 
254  std::cout << "DC OP in: " << timeTaken * 1e-6 << " ms (" << timeTaken
255  << " ns)" << std::endl;
256  }
257 
265  void simulate() {
266  constexpr VT convergedThreshold = 1e-12;
267  constexpr size_t maxNR = 32;
268  Matrix<VT> tempSoln = Matrix<VT>(solutionMat.M, 1);
269  VT maxDiff;
270  VT singleVarDiff;
271  auto simStartTime = std::chrono::high_resolution_clock::now();
272  for (size_t n = 1; n < steps; n++) {
273  size_t nr;
274  for (nr = 0; nr < maxNR; nr++) {
275  auto & stamp = elements.generateNonLinearStamp(solutionMat, n,
276  timestep);
277  stamp.G.luPair(luPair);
278  stamp.G.leftDivide(stamp.s, luPair, scratchSpace, tempSoln);
279 
280  maxDiff = 0;
281  for (size_t k = 0; k < solutionMat.M; k++) {
282  singleVarDiff = std::abs(solutionMat(k, n) - tempSoln(k, 0));
283  maxDiff = std::max(maxDiff, singleVarDiff);
284  }
285 
286  for (size_t k = 0; k < solutionMat.M; k++) {
287 #ifdef _DEBUG
288  if (std::isnan(tempSoln(k, 0))) {
289  std::cout << "Simulation Error: NaN found in solution"
290  << std::endl;
291  }
292 #endif
293  solutionMat(k, n) = tempSoln(k, 0);
294  }
295  if (maxDiff < convergedThreshold) {
296  break;
297  }
299  }
300 
301 #ifdef _DEBUG
302  if (nr < maxNR) {
303  std::cout << "NR terminated at: " << nr << " steps" << std::endl;
304  }
305 #endif
306 
308  if (n == 1) {
309  elements.staticStampIsFresh = false; // for VF s-param model update
310  }
311  }
312  auto simEndTime = std::chrono::high_resolution_clock::now();
313  auto timeTaken = std::chrono::duration_cast<std::chrono::nanoseconds>(
314  simEndTime - simStartTime)
315  .count();
316  // std::cout << "Time taken for simulation: " << timeTaken << std::endl;
317  std::cout << timeTaken * 1e-6 << " ms (" << timeTaken << " ns)" << std::endl;
318  std::ofstream runtimeFile("RunTimes.txt", std::ofstream::app);
319  runtimeFile << netlistPath << " " << timeTaken << std::endl;
320 
321  dataDump();
322 
323  size_t graphNum = 1;
324  for (auto nodes : nodesToGraph) {
325  printMultipleOnGraph(nodes, std::to_string(graphNum++));
326  }
327  }
328 
333  void printGraph(size_t node) {
334 #ifdef WITH_PYTHON
335  namespace plt = matplotlibcpp;
336  assert(node > 0);
337  std::vector<double> timeVector(steps);
338  std::vector<double> voltageNode(steps);
339  for (size_t n = 0; n < steps; n++) {
340  voltageNode[n] = solutionMat(node - 1, n);
341  timeVector[n] = n * timestep;
342  }
343  // Set the size of output image to 1200x780 pixels
344  plt::figure_size(1200, 780);
345  // Plot line from given x and y data. Color is selected automatically.
346  plt::plot(timeVector, voltageNode);
347 
348  std::string filename = "Node " + std::to_string(node) + ".png";
349  std::string filenameEps = "Node " + std::to_string(node) + ".eps";
350  // std::cout << "Saving result to " << filename << std::endl;
351  plt::save(filename.c_str());
352  plt::save(filenameEps.c_str());
353  plt::close();
354 #endif
355  }
356 
362  void printMultipleOnGraph(std::vector<size_t> nodeVec, std::string suffix = "") {
363 #ifdef WITH_PYTHON
364  namespace plt = matplotlibcpp;
365  plt::figure_size(1200, 780);
366  for (auto node : nodeVec) {
367  assert(node > 0);
368  std::vector<double> timeVector(steps);
369  std::vector<double> voltageNode(steps);
370  for (size_t n = 0; n < steps; n++) {
371  voltageNode[n] = solutionMat(node - 1, n);
372  timeVector[n] = n * timestep;
373  }
374  // Set the size of output image to 1200x780 pixels
375  // Plot line from given x and y data. Color is selected automatically.
376  std::map<std::string, std::string> kwArgs;
377  kwArgs["label"] = "Node " + std::to_string(node);
378  plt::plot(timeVector, voltageNode, kwArgs);
379  }
380  plt::legend();
381  std::string name = "Graph";
382  name += suffix;
383 
384  plt::save(name + ".png");
385  plt::save(name + ".eps");
386  plt::close();
387 #endif
388  }
389 
392  void dataDump() {
393 #ifdef WITH_MATLAB
394  // Create matlab data array factory
395  matlab::data::ArrayFactory factory;
396 
397  std::vector<std::string> varNames = {"t"};
398 #endif
399 
400  std::ofstream outputFile(outputFilePath);
401  outputFile << "time";
402  for (int i = 1; i <= numNodes; i++) {
403  outputFile << "\t"
404  << "n" << i;
405 #ifdef WITH_MATLAB
406  if (matlabDesktop) {
407  varNames.emplace_back(std::string("n") + std::to_string(i));
408  }
409 #endif
410  }
411 
412  for (int i = 1; i <= numCurrents; i++) {
413  outputFile << "\t"
414  << "i" << i;
415 #ifdef WITH_MATLAB
416  if (matlabDesktop) {
417  varNames.emplace_back(std::string("i") + std::to_string(i));
418  }
419 #endif
420  }
421 
422 #ifdef WITH_MATLAB
423  auto sArray = factory.createStructArray({solutionMat.N, 1}, varNames);
424 #endif
425  for (size_t n = 0; n < solutionMat.N; n++) {
426  outputFile << std::endl;
427  outputFile << std::setprecision(9) << n * timestep;
428 #ifdef WITH_MATLAB
429  if (matlabDesktop) {
430  sArray[n][varNames[0]] = factory.createArray({1, 1}, {n * timestep});
431  }
432 #endif
433  for (int i = 0; i < numNodes + numCurrents; i++) {
434  outputFile << "\t" << std::setprecision(9) << solutionMat(i, n);
435 #ifdef WITH_MATLAB
436  if (matlabDesktop) {
437  sArray[n][varNames[i + 1]] = factory
438  .createArray({1, 1},
439  {solutionMat(i,
440  n)});
441  }
442 #endif
443  }
444  }
445  outputFile.close();
446 
447 #ifdef WITH_MATLAB
448  if (matlabDesktop) {
449  matlabEngine->setVariable(u"solutionData", sArray,
450  matlab::engine::WorkspaceType::GLOBAL);
451  }
452 #endif
453  }
454 
455 
456  private:
457 #ifdef WITH_MATLAB
458  std::shared_ptr<matlab::engine::MATLABEngine> matlabEngine;
459  bool matlabDesktop = false;
460 #endif
461 
462 
466  void parseGraph(std::string line) {
467  std::stringstream sstr(line);
468  nodesToGraph.emplace_back(std::vector<size_t>());
469  std::vector<size_t> & toGraph = nodesToGraph.back();
470  size_t nodeNum = 0;
471  sstr >> nodeNum;
472 
473  while (!sstr.fail()) {
474  toGraph.emplace_back(nodeNum);
475  sstr.ignore(1);
476  sstr >> nodeNum;
477  }
478  }
479 
480  std::string outputFilePath = "datadump.txt";
481  std::string netlistPath = "";
482 
483  double initialTime;
484  double timestep;
485  double finalTime;
486  size_t steps;
487 
488  size_t numNodes = 1;
489  size_t numCurrents = 0;
490  size_t numDCCurrents = 0;
491  bool performDCAnalysis = true;
494 
500 
502  std::vector<std::vector<size_t> > nodesToGraph;
503 
506 };
507 
508 #endif
SourceType
The type of (voltage) source.
Definition: Simulator.hpp:37
LineType
The first character of each line for each component type.
Definition: Simulator.hpp:23
@ SParameterBlock
The main class to hold all of the relevant simulation data.
Definition: Simulator.hpp:46
CircuitElements< VT > elements
A collection of all the circuit elements.
Definition: Simulator.hpp:493
void simulate()
Where a lot of the magic starts. This is what runs the simulation.
Definition: Simulator.hpp:265
std::string netlistPath
Definition: Simulator.hpp:481
Matrix< VT > scratchSpace
Preallocated space to prevent repeated allocations and deallocations. using during the leftDivide to ...
Definition: Simulator.hpp:499
std::string outputFilePath
Definition: Simulator.hpp:480
std::vector< std::vector< size_t > > nodesToGraph
Keeps track of the nodes to be graphed after simulation.
Definition: Simulator.hpp:502
void printGraph(size_t node)
Outputs a single node's (or current's) time series to a graph, saving as both eps and png.
Definition: Simulator.hpp:333
LUPair< VT > luPair
Preallocated space to prevent repeated allocations and deallocations.
Definition: Simulator.hpp:496
void dataDump()
Simple function to dump the output of the simulator in a matlab table readable format.
Definition: Simulator.hpp:392
SimulationEnvironment(std::string netlistPath)
Setup the simulation environment from a netlist.
Definition: Simulator.hpp:52
void printMultipleOnGraph(std::vector< size_t > nodeVec, std::string suffix="")
Similar to printGraph, but instead can plot multiple series on the same graph.
Definition: Simulator.hpp:362
Matrix< VT > solutionMat
Preallocated space to store the results in.
Definition: Simulator.hpp:505
void parseGraph(std::string line)
Helper function to pull indices from graph netlist directive.
Definition: Simulator.hpp:466
void setDCOpPoint()
a function to determine and set the DC operating point
Definition: Simulator.hpp:230
plot(d.time, d.(nodeNum))
for i
for k
Definition: interch.m:15
end if abs(real(dotprod))>rstoerst rstoerst
static void addToElements(const std::string &line, CircuitElements< T > &elements, size_t &numNodes, size_t &numCurrents, size_t &numDCCurrents)
Definition: BJT.hpp:119
static void addToElements(const std::string &line, CircuitElements< T > &elements, size_t &numNodes, size_t &numCurrents, size_t &numDCCurrents)
Definition: BJT.hpp:266
An ideal capacitor model.
Definition: Capacitor.hpp:11
static void addToElements(const std::string &line, CircuitElements< T > &elements, size_t &numNodes, size_t &numCurrents, size_t &numDCCurrents)
Definition: Capacitor.hpp:97
void updateDCStoredState(const Matrix< T > &solutionVector, size_t numCurrents)
Updates the components based on their DC value. Applies to dynamic and non-linear components.
std::vector< std::shared_ptr< Component< T > > > dynamicElements
A container to store the dynamic components.
std::vector< std::shared_ptr< Component< T > > > staticElements
A container to store the static components.
Stamp< T > staticStamp
Preallocated stamp. Used for caching between loop iterations. Static stamps will only be generated on...
bool staticStampIsFresh
A variable used to track if the cached stamp is current.
Stamp< T > & generateNonLinearStamp(const Matrix< T > &solutionMatrix, const size_t currentSolutionIndex, T timestep)
Obtains the dynamic stamp, then adds dynamic components to it.
std::vector< std::shared_ptr< Component< T > > > nonLinearElements
A container to store the Non-Linear components.
void setNewStampSize(size_t numNodes, size_t numCurrents, size_t numDCCurrents=0)
Updates the size of all stamps.
bool nonLinearStampIsFresh
A variable used to track if the cached stamp is current.
Stamp< T > & generateDCStamp(const Matrix< T > &solutionVector, size_t numCurrents)
Generates the complete DC stamp.
void updateTimeStep(const Matrix< T > &solutionMatrix, const size_t currentSolutionIndex, T timestep)
Updates the components at the end of each time step. Applies to dynamic and non-linear components.
An ideal current source.
static void addToElements(const std::string &line, CircuitElements< T > &elements, size_t &numNodes, size_t &numCurrents, size_t &numDCCurrents)
An ebbers moll diode model.
Definition: Diode.hpp:11
static void addToElements(const std::string &line, CircuitElements< T > &elements, size_t &numNodes, size_t &numCurrents, size_t &numDCCurrents)
Definition: Diode.hpp:71
An ideal inductor model.
Definition: Inductor.hpp:11
static void addToElements(const std::string &line, CircuitElements< T > &elements, size_t &numNodes, size_t &numCurrents, size_t &numDCCurrents)
Definition: Inductor.hpp:114
size_t N
size_t M
static void addToElements(const std::string &line, CircuitElements< T > &elements, size_t &numNodes, size_t &numCurrents, size_t &numDCCurrents)
static void addToElements(const std::string &line, CircuitElements< T > &elements, size_t &numNodes, size_t &numCurrents, size_t &numDCCurrents)
static void addToElements(const std::string &line, CircuitElements< T > &elements, size_t &numNodes, size_t &numCurrents, size_t &numDCCurrents)
Definition: NLNMOS.hpp:181
An ideal resistor model.
Definition: Resistor.hpp:10
static void addToElements(const std::string &line, CircuitElements< T > &elements, size_t &numNodes, size_t &numCurrents, size_t &numDCCurrents)
Definition: Resistor.hpp:60
A DTIR based model of an s-parameter block.
static void addToElements(const std::string &line, CircuitElements< T > &elements, size_t &numNodes, size_t &numCurrents, size_t &numDCCurrents)
static void addToElements(const std::string &line, CircuitElements< T > &elements, size_t &numNodes, size_t &numCurrents, size_t &numDCCurrents)
A sinusoidal voltage source model.
A time series voltage source model.
An ideal voltageSource.
static void addToElements(const std::string &line, CircuitElements< T > &elements, size_t &numNodes, size_t &numCurrents, size_t &numDCCurrents)