JUK1
SParameterBlockVF.hpp
Go to the documentation of this file.
1 #ifndef _SPARAMETERBLOCKVF_HPP_INC_
2 #define _SPARAMETERBLOCKVF_HPP_INC_
3 #include <iostream>
4 #include <fstream>
5 #include <sstream>
6 #include <complex>
7 
10 #include "Maths/dft.hpp"
11 #include "Maths/ForceCausal.hpp"
12 
13 #ifdef WITH_MATLAB
14 #include "MatlabEngine.hpp"
15 #include "MatlabDataArray.hpp"
16 #endif
17 
18 template<typename T>
20  size_t numPoles = 0;
21 
22  std::vector<std::complex<T> > pole;
23  std::vector<std::complex<T> > residue;
24  std::complex<T> remainder = 0;
25 
27  std::vector<std::complex<T> > lambda_p;
29  std::vector<std::complex<T> > mu_p;
31  std::vector<std::complex<T> > nu_p;
32 
33  std::vector<std::complex<T> > exp_alpha;
34 
36  std::complex<T> lambda = 0;
38  std::complex<T> mu = 0;
40  std::complex<T> nu = 0;
41 
43  std::vector<std::complex<T> > x;
44 };
45 
50 template<typename T>
53  size_t positive = 0;
55  size_t negative = 0;
57  size_t current = 0;
58 
59 
61  std::complex<T> beta = 0;
62 
65  std::vector<std::complex<T> > alpha;
66 
68  std::complex<T> R = 0;
69 
70  std::vector<SParamVFDataFrom<T> > from;
71 };
72 
73 
77 template<typename T>
78 struct SParameterBlockVF : public Component<T> {
79  std::vector<SParameterPortVF<T> > port;
80  size_t numPorts = 0;
81  bool firstOrder = true;
82 
83  T z_ref = 0;
84 
85  std::complex<T> history_p(const size_t p, const Matrix<T> & solutionMatrix,
86  const size_t currentSolutionIndex, T timestep,
87  const size_t sizeG_A) const {
88  std::complex<T> toRet = 0;
89  for (size_t c = 0; c < numPorts; c++) {
90  for (size_t rho = 0; rho < port[p].from[c].numPoles; rho++) {
91  toRet += port[p].from[c].x[rho] * port[p].from[c].exp_alpha[rho];
92  }
93 
94  toRet += port[p].from[c].mu *
95  awave_p(c, solutionMatrix, currentSolutionIndex - 1, sizeG_A);
96  if (currentSolutionIndex > 1) {
97  toRet += port[p].from[c].nu * awave_p(c, solutionMatrix,
98  currentSolutionIndex - 2,
99  sizeG_A);
100  }
101  }
102  return 2.0 * toRet * std::sqrt(z_ref);
103  }
104 
105  T
106  V_p(const size_t p, const Matrix<T> & solutionMatrix,
107  const size_t currentSolutionIndex, T timestep, const size_t sizeG_A) const {
108  return std::real(
109  history_p(p, solutionMatrix, currentSolutionIndex, timestep, sizeG_A) *
110  port[p].beta);
111  }
112 
113  T awave_p(const size_t p, const Matrix<T> & solutionMatrix,
114  const size_t currentSolutionIndex, const size_t sizeG_A) const {
115  size_t np = port[p].positive - 1;
116  size_t nn = port[p].negative - 1;
117  size_t curr = port[p].current - 1;
118  size_t n = currentSolutionIndex;
119  T toRet = 0;
120 
121  if (port[p].positive) {
122  toRet += solutionMatrix(np, n);
123  }
124 
125  if (port[p].negative) {
126  toRet += -solutionMatrix(nn, n);
127  }
128 
129  return (toRet + solutionMatrix(sizeG_A + curr, n) * z_ref) /
130  (2 * std::sqrt(z_ref));
131  }
132 
133  T bwave_p(const size_t p, const Matrix<T> & solutionMatrix,
134  const size_t currentSolutionIndex, const size_t sizeG_A) const {
135  size_t np = port[p].positive - 1;
136  size_t nn = port[p].negative - 1;
137  size_t curr = port[p].current - 1;
138  size_t n = currentSolutionIndex;
139  T toRet = 0;
140 
141  if (port[p].positive) {
142  toRet += solutionMatrix(np, n);
143  }
144 
145  if (port[p].negative) {
146  toRet += -solutionMatrix(nn, n);
147  }
148 
149  return (toRet - solutionMatrix(sizeG_A + curr, n) * z_ref) /
150  (2 * std::sqrt(z_ref));
151  }
152 
153  void addStaticStampTo(Stamp<T> & stamp) const {
154  for (size_t p = 0; p < port.size(); p++) {
155  size_t np = port[p].positive - 1;
156  size_t nn = port[p].negative - 1;
157  size_t curr = port[p].current - 1;
158  // Voltage source and resistance
159  //
160  stamp.G(stamp.sizeG_A + curr,
161  stamp.sizeG_A + curr) += std::real(-port[p].R);
162 
163  if (port[p].positive != 0) {
164  stamp.G(stamp.sizeG_A + curr, np) += 1;
165  stamp.G(np, stamp.sizeG_A + curr) += 1;
166  }
167 
168  if (port[p].negative != 0) {
169  stamp.G(stamp.sizeG_A + curr, nn) += -1;
170  stamp.G(nn, stamp.sizeG_A + curr) += -1;
171  }
172  // controlled sources
173  for (size_t c = 0; c < port.size(); c++) {
174  if (c != p) {
175  if (port[c].positive != 0) {
176  stamp.G(stamp.sizeG_A + curr,
177  port[c].positive -
178  1) += std::real(-port[p].alpha[c]);
179  }
180  if (port[c].negative != 0) {
181  stamp.G(stamp.sizeG_A + curr,
182  port[c].negative - 1) += std::real(port[p].alpha[c]);
183  }
184  stamp.G(stamp.sizeG_A + curr,
185  stamp.sizeG_A + port[c].current -
186  1) += std::real(-z_ref * port[p].alpha[c]);
187  }
188  }
189  }
190  }
191 
192  void addDynamicStampTo(Stamp<T> & stamp, const Matrix<T> & solutionMatrix,
193  const size_t currentSolutionIndex,
194  T simulationTimestep) const {
195  for (size_t p = 0; p < port.size(); p++) {
196  size_t curr = port[p].current - 1;
197  // V_p
198  stamp.s(stamp.sizeG_A + curr,
199  0) += V_p(p, solutionMatrix, currentSolutionIndex,
200  simulationTimestep, stamp.sizeG_A);
201  }
202  }
203 
204  void updateStoredState(const Matrix<T> & solutionMatrix,
205  const size_t currentSolutionIndex, T timestep,
206  size_t sizeG_A) {
207  for (size_t p = 0; p < numPorts; p++) {
208  for (size_t c = 0; c < numPorts; c++) {
209  for (size_t rho = 0; rho < port[p].from[c].numPoles; rho++) {
210  port[p].from[c].x[rho] = port[p].from[c].x[rho] *
211  port[p].from[c].exp_alpha[rho] +
212  port[p].from[c].lambda_p[rho] *
213  awave_p(c, solutionMatrix,
214  currentSolutionIndex,
215  sizeG_A) +
216  port[p].from[c].mu_p[rho] *
217  awave_p(c, solutionMatrix,
218  currentSolutionIndex - 1,
219  sizeG_A);
220  if (!firstOrder) {
221  port[p].from[c].x[rho] += port[p].from[c].nu_p[rho] *
222  awave_p(c, solutionMatrix,
223  currentSolutionIndex - 2,
224  sizeG_A);
225  }
226  }
227  }
228  }
229 
230  if (firstOrder && currentSolutionIndex >= 1) {
231  setSecondOrder(timestep);
232  }
233  }
234 
235  void setConstants(T timestep) {
236  for (size_t p = 0; p < numPorts; p++) {
237  port[p].beta = 1.0 - port[p].from[p].lambda - port[p].from[p].remainder;
238  port[p].beta = 1.0 / port[p].beta;
239 
240  port[p].R = 1.0 + port[p].from[p].lambda + port[p].from[p].remainder;
241 
242  port[p].R = z_ref * port[p].R * port[p].beta;
243 
244  for (size_t c = 0; c < numPorts; c++) {
245  if (c == p) {
246  port[p].alpha[c] = 0;
247  } else {
248  port[p].alpha[c] = port[p].from[c].lambda +
249  port[p].from[c].remainder;
250  port[p].alpha[c] = port[p].alpha[c] * port[p].beta;
251  }
252  }
253  }
254  }
255 
256  void setFirstOrder(T timestep) {
257  firstOrder = true;
258 
259  for (size_t p = 0; p < numPorts; p++) {
260  for (size_t c = 0; c < numPorts; c++) {
261  port[p].from[c].lambda = 0;
262  port[p].from[c].mu = 0;
263  port[p].from[c].nu = 0;
264  for (size_t rho = 0; rho < port[p].from[c].numPoles; rho++) {
265  const auto & pole = port[p].from[c].pole[rho];
266  const auto & residue = port[p].from[c].residue[rho];
267  const auto a = pole * timestep;
268  const auto ea = std::exp(a);
269  port[p].from[c].lambda_p[rho] = -(residue / pole) *
270  (1.0 + (1.0 - ea) / (a));
271  port[p].from[c].lambda += port[p].from[c].lambda_p[rho];
272 
273  port[p].from[c].mu_p[rho] = -(residue / pole) *
274  ((ea - 1.0) / a - ea);
275  port[p].from[c].mu += port[p].from[c].mu_p[rho];
276 
277  port[p].from[c].nu_p[rho] = 0;
278  }
279  }
280  }
281 
282  setConstants(timestep);
283  }
284 
285  void setSecondOrder(T timestep) {
286  firstOrder = false;
287 
288  for (size_t p = 0; p < numPorts; p++) {
289  for (size_t c = 0; c < numPorts; c++) {
290  port[p].from[c].lambda = 0;
291  port[p].from[c].mu = 0;
292  port[p].from[c].nu = 0;
293  for (size_t rho = 0; rho < port[p].from[c].numPoles; rho++) {
294  const auto & pole = port[p].from[c].pole[rho];
295  const auto & residue = port[p].from[c].residue[rho];
296  const auto a = pole * timestep;
297  const auto ea = std::exp(a);
298  port[p].from[c].lambda_p[rho] = -(residue / pole) *
299  ((1.0 - ea) / (a * a) +
300  (3.0 - ea) / (2.0 * a) + 1.0);
301  port[p].from[c].lambda += port[p].from[c].lambda_p[rho];
302 
303  port[p].from[c].mu_p[rho] = -(residue / pole) *
304  (-2.0 * (1.0 - ea) / (a * a) -
305  (2.0 / a) - ea);
306  port[p].from[c].mu += port[p].from[c].mu_p[rho];
307 
308  port[p].from[c].nu_p[rho] = -(residue / pole) *
309  ((1.0 - ea) / (a * a) +
310  (1.0 + ea) / (2.0 * a));
311  port[p].from[c].nu += port[p].from[c].nu_p[rho];
312  }
313  }
314  }
315 
316  setConstants(timestep);
317  }
318 
319  void setTimestep(T timestep) {
320  for (size_t p = 0; p < numPorts; p++) {
321  for (size_t c = 0; c < numPorts; c++) {
322  for (size_t rho = 0; rho < port[p].from[c].numPoles; rho++) {
323  port[p].from[c].exp_alpha[rho] = std::exp(
324  port[p].from[c].pole[rho] * timestep);
325  }
326  }
327 
328  if (firstOrder) {
329  setFirstOrder(timestep);
330  } else {
331  setSecondOrder(timestep);
332  }
333  }
334  }
335 #ifdef WITH_MATLAB
336  void
337  performVectorFit(std::string filePath, size_t numPorts,
338  std::shared_ptr<matlab::engine::MATLABEngine> matlabEngine) {
339  matlab::data::ArrayFactory factory;
340 
341  matlabEngine->eval(u"addpath('./Matlab');");
342  std::vector<matlab::data::Array> args;
343  args.push_back(factory.createCharArray(filePath));
344  auto result = matlabEngine->feval(u"CPPVectFitAdaptor", 3, args);
345 
346  assert(result[0].getDimensions()[0] == numPorts);
347  assert(result[0].getDimensions()[1] == numPorts);
348 
349  z_ref = static_cast<T>(result[1][0]);
350  for (size_t a = 0; a < numPorts; a++) {
351  port[a].alpha.resize(numPorts);
352  port[a].from.resize(numPorts);
353  for (size_t b = 0; b < numPorts; b++) {
354  auto structArrayResult = static_cast<
355  matlab::data::TypedArray<matlab::data::Struct> >(result[0]);
356  auto structResult = static_cast<matlab::data::Struct>(
357  structArrayResult[a][b]);
358 
359  port[a].from[b].numPoles = structResult["poles"]
360  .getNumberOfElements();
361  port[a].from[b].lambda_p.resize(port[a].from[b].numPoles);
362  port[a].from[b].mu_p.resize(port[a].from[b].numPoles);
363  port[a].from[b].nu_p.resize(port[a].from[b].numPoles);
364  port[a].from[b].exp_alpha.resize(port[a].from[b].numPoles);
365  port[a].from[b].x.resize(port[a].from[b].numPoles);
366  port[a].from[b].pole.resize(port[a].from[b].numPoles);
367  port[a].from[b].residue.resize(port[a].from[b].numPoles);
368 
369  for (size_t p = 0; p < port[a].from[b].numPoles; p++) {
370  port[a].from[b].pole[p] = static_cast<std::complex<T> >(
371  static_cast<matlab::data::TypedArray<std::complex<T> > >(
372  structResult["poles"])[p]);
373  port[a].from[b].residue[p] = static_cast<std::complex<T> >(
374  static_cast<matlab::data::TypedArray<std::complex<T> > >(
375  structResult["residues"])[p]);
376  }
377  port[a].from[b].remainder = static_cast<std::complex<T> >(
378  static_cast<matlab::data::TypedArray<std::complex<T> > >(
379  structResult["remainder"])[0]);
380  }
381  }
382  }
383 #endif
384 
385  void readInPRR(std::string filePath, size_t numPorts) {
386  std::ifstream file(filePath);
387 
388 
389  std::string line;
390  while (file.peek() == '#' || file.peek() == '!') {
391  std::getline(file, line);
392  }
393 
394  T rval;
395  T cval;
396 
397  std::stringstream polesLine;
398  std::stringstream residuesLine;
399 
400  file >> z_ref;
401 
402  while (!file.eof()) {
403  for (size_t a = 0; a < numPorts; a++) {
404  port[a].alpha.resize(numPorts);
405  port[a].from.resize(numPorts);
406  for (size_t c = 0; c < numPorts; c++) {
407  file >> rval >> cval;
408  port[a].from[c].remainder = std::complex(rval, cval);
409 
410  if (file.fail()) {
411  break;
412  }
413 
414  std::
415  getline(file,
416  line); // have to clear the end of the remainder line
417  std::getline(file, line);
418  polesLine = std::stringstream(line);
419  std::getline(file, line);
420  residuesLine = std::stringstream(line);
421 
422  while (!polesLine.eof() && !residuesLine.eof()) {
423  polesLine >> rval >> cval;
424  port[a].from[c].pole.emplace_back(rval, cval);
425 
426  residuesLine >> rval >> cval;
427  port[a].from[c].residue.emplace_back(rval, cval);
428  }
429 
430  port[a].from[c].numPoles = port[a].from[c].pole.size();
431  port[a].from[c].lambda_p.resize(port[a].from[c].numPoles);
432  port[a].from[c].mu_p.resize(port[a].from[c].numPoles);
433  port[a].from[c].nu_p.resize(port[a].from[c].numPoles);
434  port[a].from[c].exp_alpha.resize(port[a].from[c].numPoles);
435  port[a].from[c].x.resize(port[a].from[c].numPoles);
436  }
437  }
438 
439  if (file.fail()) {
440  break;
441  }
442  }
443  }
444 
445  void addDCAnalysisStampTo(Stamp<T> & stamp, const Matrix<T> & solutionVector,
446  size_t numCurrents) const {
447  size_t numPorts = port.size();
448  std::vector<std::complex<T> > xSum(port.size() * port.size());
449  for (size_t p = 0; p < port.size(); p++) {
450  for (size_t c = 0; c < port.size(); c++) {
451  for (size_t rho = 0; rho < port[p].from[c].numPoles; rho++) {
452  xSum[p * numPorts + c] += -(port[p].from[c].lambda_p[rho] +
453  port[p].from[c].mu_p[rho]) /
454  (port[p].from[c].exp_alpha[rho] - 1.0);
455  }
456  xSum[p * numPorts + c] += port[p].from[c].remainder;
457  }
458  }
459 
460  for (size_t p = 0; p < port.size(); p++) {
461  size_t np = port[p].positive - 1;
462  size_t nn = port[p].negative - 1;
463  size_t curr = port[p].current - 1;
464 
465  std::complex<T> beta = 1.0 / (1.0 - xSum[p * numPorts + p]);
466 
467  stamp.G(stamp.sizeG_A + curr, stamp.sizeG_A + curr) += std::real(
468  -z_ref * (1.0 + xSum[p * numPorts + p]) * beta);
469 
470  if (port[p].positive != 0) {
471  stamp.G(stamp.sizeG_A + curr, np) += 1;
472  stamp.G(np, stamp.sizeG_A + curr) += 1;
473  }
474 
475  if (port[p].negative != 0) {
476  stamp.G(stamp.sizeG_A + curr, nn) += -1;
477  stamp.G(nn, stamp.sizeG_A + curr) += -1;
478  }
479  // controlled sources
480  for (size_t c = 0; c < port.size(); c++) {
481  if (c != p) {
482  if (port[c].positive != 0) {
483  stamp.G(stamp.sizeG_A + curr,
484  port[c].positive -
485  1) += std::real(-xSum[p * numPorts + c] * beta);
486  }
487  if (port[c].negative != 0) {
488  stamp.G(stamp.sizeG_A + curr,
489  port[c].negative -
490  1) += std::real(xSum[p * numPorts + c] * beta);
491  }
492  stamp.G(stamp.sizeG_A + curr,
493  stamp.sizeG_A + port[c].current -
494  1) += std::real(-z_ref * xSum[p * numPorts + c] *
495  beta);
496  }
497  }
498  }
499  }
500 
501  static void
502  addToElements(const std::string & line, CircuitElements<T> & elements,
503  size_t & numNodes, size_t & numCurrents, size_t & numDCCurrents) {
504  // std::regex SParameterBlockInitialRegex( R"(^S(.*?)\s(\d+?)\s)" );
505  std::regex SParameterBlockInitialRegex = generateRegex("SV", "n s", true,
506  false);
507  std::smatch matches;
508  std::regex_search(line, matches, SParameterBlockInitialRegex);
509 
510  SParameterBlockVF<T> block;
511 
512  block.designator = "SV";
513  block.designator += matches.str(1);
514 
515  size_t numPorts = std::stoul(matches.str(2));
516  block.numPorts = numPorts;
517  block.port = std::vector<SParameterPortVF<T> >(numPorts);
518 
519  auto strIter = matches.suffix().first;
520  std::regex SParameterBlockPortRegex(R"(^(\d+?)\s(\d+?)\s)");
521  for (size_t p = 0; p < numPorts; p++) {
522  std::regex_search(strIter, line.cend(), matches,
523  SParameterBlockPortRegex);
524  block.port[p].positive = std::stoi(matches.str(1));
525  block.port[p].negative = std::stoi(matches.str(2));
526 
527  numNodes = std::max(numNodes, std::stoull(matches.str(1)));
528  numNodes = std::max(numNodes, std::stoull(matches.str(2)));
529 
530  block.port[p].current = ++numCurrents;
531 
532  strIter = matches.suffix().first;
533  }
534  std::regex endOfLineRegex(R"(^(.*)$)");
535  std::regex_search(strIter, line.cend(), matches, endOfLineRegex);
536  if (line[2] == 'F') {
537 #ifdef WITH_MATLAB
538  block.performVectorFit(matches.str(1), numPorts, elements.matlabEngine);
539 #endif
540  } else {
541  block.readInPRR(matches.str(1), numPorts);
542  }
543 
544  elements.dynamicElements.emplace_back(
545  std::make_shared<SParameterBlockVF<T> >(block));
546  for (size_t p = 0; p < numPorts; p++) {
547  elements.nodeComponentMap.insert(
548  {{block.port[p].positive, elements.dynamicElements.back()},
549  {block.port[p].negative, elements.dynamicElements.back()}});
550  }
551  }
552 };
553 
554 #endif
std::regex generateRegex(std::string indentifier, std::string simplifiedMatching, bool startAnchor=true, bool endAnchor=true)
a helper function to aid in the construction of regexes for parsing netlist files
common weighting for all matrix elements
Definition: QPpassive.m:47
Find a complex rational model for freq domain data for a
b
Definition: getResidues.m:47
Number of poles to use in fit numPoles
Definition: getResidues.m:14
a glorified container for the different types of components.
A template base class to define the fundamental things a component should define.
Definition: Component.hpp:108
std::string designator
The designator as in the netlist for e.g.
Definition: Component.hpp:110
A matrix class with support for LU-decomposition, and left division.
std::complex< T > nu
the contribution of the 2nd previous awave
std::complex< T > remainder
std::vector< std::complex< T > > pole
std::vector< std::complex< T > > nu_p
the per-pole contribution of the 2nd previous awave
std::vector< std::complex< T > > exp_alpha
std::vector< std::complex< T > > lambda_p
the per-pole contribution of the current awave
std::vector< std::complex< T > > x
The previous x values.
std::complex< T > lambda
the contribution of the current awave
std::vector< std::complex< T > > residue
std::complex< T > mu
the contribution of the previous awave
std::vector< std::complex< T > > mu_p
the per-pole contribution of the previous awave
A vectorfitting based model of an s-parameter block.
std::vector< SParameterPortVF< T > > port
void addDynamicStampTo(Stamp< T > &stamp, const Matrix< T > &solutionMatrix, const size_t currentSolutionIndex, T simulationTimestep) const
Adds this component's dynamic stamp to the target stamp.
void setTimestep(T timestep)
initialises the component
void updateStoredState(const Matrix< T > &solutionMatrix, const size_t currentSolutionIndex, T timestep, size_t sizeG_A)
Updates any stored state based on the current solution index.
T V_p(const size_t p, const Matrix< T > &solutionMatrix, const size_t currentSolutionIndex, T timestep, const size_t sizeG_A) const
void addStaticStampTo(Stamp< T > &stamp) const
Adds this component's static stamp to the target stamp.
std::complex< T > history_p(const size_t p, const Matrix< T > &solutionMatrix, const size_t currentSolutionIndex, T timestep, const size_t sizeG_A) const
void readInPRR(std::string filePath, size_t numPorts)
void setSecondOrder(T timestep)
T awave_p(const size_t p, const Matrix< T > &solutionMatrix, const size_t currentSolutionIndex, const size_t sizeG_A) const
static void addToElements(const std::string &line, CircuitElements< T > &elements, size_t &numNodes, size_t &numCurrents, size_t &numDCCurrents)
T bwave_p(const size_t p, const Matrix< T > &solutionMatrix, const size_t currentSolutionIndex, const size_t sizeG_A) const
void setConstants(T timestep)
void addDCAnalysisStampTo(Stamp< T > &stamp, const Matrix< T > &solutionVector, size_t numCurrents) const
adds this component's DC stamp to the target stamp.
void setFirstOrder(T timestep)
a helper struct to store the information for the ports of an S-Parameter Block
std::complex< T > beta
a constant factor equal to 1 / ( 1 - lambda )
std::vector< std::complex< T > > alpha
The equivalent scalar for controlled sources. Index is the second port.
size_t current
The current index.
std::vector< SParamVFDataFrom< T > > from
size_t positive
The positive index.
size_t negative
The negative index.
std::complex< T > R
The equivalent resistance.
A helper struct to store the preallocated stamps for MNA.
Definition: Component.hpp:23
Matrix< T > s
Definition: Component.hpp:28
size_t sizeG_A
Definition: Component.hpp:24
Matrix< T > G
Definition: Component.hpp:27