JUK1
SParameterBlock.hpp
Go to the documentation of this file.
1 #ifndef _SPARAMETERBLOCK_HPP_INC_
2 #define _SPARAMETERBLOCK_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 #include <immintrin.h>
13 
14 
19 template<typename T>
22  size_t positive = 0;
24  size_t negative = 0;
26  size_t current = 0;
28  T R = 0;
30  T beta = 0;
32  std::vector<T> s0;
33 };
34 
36  size_t length;
37  size_t offset;
38 };
39 
43 template<typename T>
45  std::vector<T> _data;
46  std::vector<T> _time;
47  std::vector<SParamLengthOffset> sParamLengthOffset;
48  size_t numPorts = 0;
49 
50  size_t & length(size_t a, size_t b) {
51  return sParamLengthOffset[a * numPorts + b].length;
52  }
53 
54  const size_t length(size_t a, size_t b) const {
55  return sParamLengthOffset[a * numPorts + b].length;
56  }
57 
58  size_t & offset(size_t a, size_t b) {
59  return sParamLengthOffset[a * numPorts + b].offset;
60  }
61 
62  const size_t offset(size_t a, size_t b) const {
63  return sParamLengthOffset[a * numPorts + b].offset;
64  }
65 
66  T & data(size_t a, size_t b, size_t n) {
67  return _data[offset(a, b) + n];
68  }
69 
70  const T & data(size_t a, size_t b, size_t n) const {
71  return _data[offset(a, b) + n];
72  }
73 
74  T & time(size_t a, size_t b, size_t n) {
75  return _time[offset(a, b) + n];
76  }
77 
78  const T & time(size_t a, size_t b, size_t n) const {
79  return _time[offset(a, b) + n];
80  }
81 };
82 
86 template<typename T>
87 struct SParameterBlock : public Component<T> {
88  std::string touchstoneFilePath = "";
89  std::vector<SParameterPort<T> > port;
91 
92  T z_ref = 0;
94 
106  T aWaveConvValue(size_t portIndex, const Matrix<T> & solutionMatrix,
107  const size_t n, T sTimePoint, const T simulationTimestep,
108  size_t sizeG_A) const {
109  T kprime = sTimePoint / simulationTimestep;
110  // This check is incredibly slow because it occurs with all loops. A similar
111  // check could occur in another place when S-Parameters are first formulated
112  // maybe
113  // if ( 0 <= kprime && kprime < 1.0 ) {
114  // std::cout << "WARNING: SIMULATION TIMESTEP TOO LARGE FOR CONVOLUTION" <<
115  // std::endl;
116  //}
117  T index = n - kprime;
118  // TODO: Maybe look into changing the bounds of the convolution to avoid
119  // having this checked too much. Maybe wrap in debug ifdef.
120  if (index <= 0) {
121  return 0.0;
122  }
123 
124  size_t floor = static_cast<size_t>(index);
125  if (floor <= 0 || floor + 1 >= n) {
126  return 0.0;
127  }
128 
129  T mix = index - static_cast<T>(floor);
130 
131  T toRet;
132 
133  T uppArr[3] = {0};
134  T lowArr[3] = {0};
135 
136  if (port[portIndex].positive != 0) {
137  uppArr[0] = solutionMatrix(port[portIndex].positive - 1, floor + 1);
138  lowArr[0] = solutionMatrix(port[portIndex].positive - 1, floor);
139  }
140 
141  if (port[portIndex].negative != 0) {
142  uppArr[1] = solutionMatrix(port[portIndex].negative - 1, floor + 1);
143  lowArr[1] = solutionMatrix(port[portIndex].negative - 1, floor);
144  }
145 
146  uppArr[2] = solutionMatrix(sizeG_A + port[portIndex].current - 1, floor + 1);
147  lowArr[2] = solutionMatrix(sizeG_A + port[portIndex].current - 1, floor);
148 
149  toRet = (uppArr[0] - lowArr[0]) * mix + lowArr[0] -
150  (uppArr[1] - lowArr[1]) * mix - lowArr[1] +
151  ((uppArr[2] - lowArr[2]) * mix + lowArr[2]) * z_ref;
152  return toRet;
153  }
154 
155 
166  T V_p(size_t p, const Matrix<T> & solutionMatrix, const size_t n,
167  T simulationTimestep, size_t sizeG_A) const {
168  // V_p = beta * sum of ports ( history of port )
169  T toRet = 0;
170  // TODO: This may finally be a good place for multithreading
171  // We could have one thread per port perhaps. Mainly as the data overlap is
172  // minimal This would require having a different "toRet" for each thread and
173  // summing at the end before returning perhaps
174  for (size_t c = 0; c < port.size(); c++) {
175  // TODO: Optimise the convolution here
176  // size_t len = std::min( n, s.sParamLength );
177  for (size_t k = 1; k < s.length(p, c); k++) {
178  toRet += aWaveConvValue(c, solutionMatrix, n, s.time(p, c, k),
179  simulationTimestep, sizeG_A) *
180  s.data(p, c, k);
181  }
182  }
183  return port[p].beta * toRet;
184  }
185 
186  T R_p(size_t p) const {
187  return port[p].R;
188  }
189 
190  T beta_p(size_t p) const {
191  return port[p].beta
192  }
193 
194  void addStaticStampTo(Stamp<T> & stamp) const {
195  for (size_t p = 0; p < port.size(); p++) {
196  size_t np = port[p].positive - 1;
197  size_t nn = port[p].negative - 1;
198  size_t curr = port[p].current - 1;
199  // Voltage source and resistance
200  stamp.G(stamp.sizeG_A + curr, stamp.sizeG_A + curr) += -port[p].R;
201  if (port[p].positive != 0) {
202  stamp.G(stamp.sizeG_A + curr, np) += 1;
203  stamp.G(np, stamp.sizeG_A + curr) += 1;
204  }
205 
206  if (port[p].negative != 0) {
207  stamp.G(stamp.sizeG_A + curr, nn) += -1;
208  stamp.G(nn, stamp.sizeG_A + curr) += -1;
209  }
210  // controlled sources
211  for (size_t c = 0; c < port.size(); c++) {
212  if (c != p) {
213  T alpha = port[p].beta * port[p].s0[c];
214  if (port[c].positive != 0) {
215  stamp.G(stamp.sizeG_A + curr,
216  port[c].positive - 1) += -alpha;
217  }
218  if (port[c].negative != 0) {
219  stamp.G(stamp.sizeG_A + curr, port[c].negative - 1) += alpha;
220  }
221  stamp.G(stamp.sizeG_A + curr,
222  stamp.sizeG_A + port[c].current - 1) += -z_ref * alpha;
223  }
224  }
225  }
226  }
227 
228  void addDynamicStampTo(Stamp<T> & stamp, const Matrix<T> & solutionMatrix,
229  const size_t currentSolutionIndex,
230  T simulationTimestep) const {
231  for (size_t p = 0; p < port.size(); p++) {
232  size_t curr = port[p].current - 1;
233  // V_p
234  stamp.s(stamp.sizeG_A + curr,
235  0) += V_p(p, solutionMatrix, currentSolutionIndex,
236  simulationTimestep, stamp.sizeG_A);
237  }
238  }
239 
240  void addDCAnalysisStampTo(Stamp<T> & stamp, const Matrix<T> & solutionVector,
241  size_t numCurrents) const {
242  for (size_t p = 0; p < port.size(); p++) {
243  size_t np = port[p].positive - 1;
244  size_t nn = port[p].negative - 1;
245  size_t curr = port[p].current - 1;
246 
247  // Voltage source and resistance
248  T sppSum = 0;
249  for (size_t k = 0; k < s.length(p, p); k++) {
250  sppSum += s.data(p, p, k);
251  }
252  T Rprime = port[p].beta * z_ref * (1 + sppSum) /
253  (1 - port[p].beta * sppSum);
254  stamp.G(stamp.sizeG_A + curr, stamp.sizeG_A + curr) += Rprime;
255  if (port[p].positive != 0) {
256  stamp.G(stamp.sizeG_A + curr, np) += 1;
257  stamp.G(np, stamp.sizeG_A + curr) += 1;
258  }
259 
260  if (port[p].negative != 0) {
261  stamp.G(stamp.sizeG_A + curr, nn) += -1;
262  stamp.G(nn, stamp.sizeG_A + curr) += -1;
263  }
264 
265  // controlled sources
266  for (size_t c = 0; c < port.size(); c++) {
267  if (c != p) {
268  T alpha = port[p].beta * port[p].s0[c];
269  T alphaPrime = 0;
270 
271  for (size_t k = 0; k < s.length(p, c); k++) {
272  alphaPrime += s.data(p, c, k);
273  }
274 
275  alphaPrime = port[p].beta * alphaPrime;
276 
277  alphaPrime += alpha;
278 
279  alphaPrime = alphaPrime / (1 - port[p].beta * sppSum);
280 
281 
282  if (port[c].positive != 0) {
283  stamp.G(stamp.sizeG_A + curr,
284  port[c].positive - 1) += -alphaPrime;
285  }
286  if (port[c].negative != 0) {
287  stamp.G(stamp.sizeG_A + curr,
288  port[c].negative - 1) += alphaPrime;
289  }
290  stamp.G(stamp.sizeG_A + curr, stamp.sizeG_A + port[c].current -
291  1) += -z_ref * alphaPrime;
292  }
293  }
294 
295  // V_p = 0
296  }
297  }
298 
299  void updateStoredState(const Matrix<T> & solutionMatrix,
300  const size_t currentSolutionIndex, T timestep,
301  size_t sizeG_A) {
302  }
303 
304 
309  using CT = std::complex<T>;
310  using FSP = std::vector<CT>;
311  std::ifstream file(touchstoneFilePath);
312 
313  std::vector<T> freqs;
314  std::vector<std::vector<FSP> > freqSParams(s.numPorts);
315  z_ref = 50;
316 
317  std::string line;
318  while (file.peek() == '#' || file.peek() == '!') {
319  std::getline(file, line);
320  }
321 
322  T val1;
323  T val2;
324  while (!file.eof()) {
325  file >> val1;
326  if (file.fail()) {
327  break;
328  }
329  freqs.emplace_back(val1);
330  for (size_t a = 0; a < s.numPorts; a++) {
331  freqSParams[a].resize(s.numPorts);
332  }
333 
334  for (size_t b = 0; b < s.numPorts; b++) {
335  for (size_t a = 0; a < s.numPorts; a++) {
336  file >> val1 >> val2;
337  freqSParams[a][b].emplace_back(val1, val2);
338  }
339  }
340  }
341 
342  // std::vector< T > symFreqs( 2 * freqs.size() - 1 );
343  // T maxFreq = freqs.back();
344  /* for ( size_t k = 0; k < freqs.size(); k++ ) {
345  symFreqs[ k ] = freqs[ k ];
346  symFreqs[ freqs.size() + k ] = maxFreq + freqs[ k ];
347  } */
348 
349  // s.sParamLength = ( 2 * freqs.size() - 2 );
350  s.sParamLengthOffset.resize(s.numPorts * s.numPorts);
351  for (size_t a = 0; a < s.numPorts; a++) {
352  port[a].s0.resize(s.numPorts);
353  for (size_t b = 0; b < s.numPorts; b++) {
354  auto causal = forceCausal(freqs, freqSParams[a][b]);
355 
356  T thresholdToKeep = 1;
357  for (auto entry : causal.data) {
358  thresholdToKeep = std::max(std::abs(entry), thresholdToKeep);
359  }
360 
361  thresholdToKeep = thresholdToKeep * fracMaxToKeep;
362 
363  s.offset(a, b) = s._data.size();
364  for (size_t n = 0; n < causal.data.size(); n++) {
365  if (n == 0 || std::abs(causal.data[n]) > thresholdToKeep) {
366  s._data.emplace_back(causal.data[n]);
367  s._time.emplace_back(n == 0 ? 0
368  : n * causal.Ts - causal.tau);
369  std::cout << s._time.back() << " " << s._data.back()
370  << std::endl;
371  }
372  }
373  s.length(a, b) = s._data.size() - s.offset(a, b);
374  std::cout << "Pruned " << ((2 * freqs.size() - 2) - s.length(a, b))
375  << " DTIR entries out of " << (2 * freqs.size() - 2)
376  << " less than " << thresholdToKeep << " ("
377  << fracMaxToKeep * 100 << "% of max val)" << std::endl;
378 
379  port[a].s0[b] = s.data(a, b, 0);
380  }
381  port[a].beta = 1.0 / (1 - s.data(a, a, 0));
382  port[a].R = port[a].beta * 50 * (1 + s.data(a, a, 0));
383  }
384  }
385 
386  static void
387  addToElements(const std::string & line, CircuitElements<T> & elements,
388  size_t & numNodes, size_t & numCurrents, size_t & numDCCurrents) {
389  // std::regex SParameterBlockInitialRegex( R"(^S(.*?)\s(\d+?)\s)" );
390  std::regex SParameterBlockInitialRegex = generateRegex("S", "w n s", true,
391  false);
392  std::smatch matches;
393  std::regex_search(line, matches, SParameterBlockInitialRegex);
394 
395  SParameterBlock<T> block;
396 
397  block.designator = "S";
398  block.designator += matches.str(1);
399 
400  block.fracMaxToKeep = std::stod(matches.str(2));
401 
402  size_t numPorts = std::stoul(matches.str(3));
403  block.s.numPorts = numPorts;
404  block.port = std::vector<SParameterPort<T> >(numPorts);
405 
406  auto strIter = matches.suffix().first;
407  std::regex SParameterBlockPortRegex(R"(^(\d+?)\s(\d+?)\s)");
408  for (size_t p = 0; p < numPorts; p++) {
409  std::regex_search(strIter, line.cend(), matches,
410  SParameterBlockPortRegex);
411  block.port[p].positive = std::stoi(matches.str(1));
412  block.port[p].negative = std::stoi(matches.str(2));
413 
414  numNodes = std::max(numNodes, std::stoull(matches.str(1)));
415  numNodes = std::max(numNodes, std::stoull(matches.str(2)));
416 
417  block.port[p].current = ++numCurrents;
418 
419  strIter = matches.suffix().first;
420  }
421  std::regex endOfLineRegex(R"(^(.*)$)");
422  std::regex_search(strIter, line.cend(), matches, endOfLineRegex);
423  block.touchstoneFilePath = matches.str(1);
424  block.readInTouchstoneFile();
425 
426  elements.dynamicElements.emplace_back(
427  std::make_shared<SParameterBlock<T> >(block));
428  for (size_t p = 0; p < numPorts; p++) {
429  elements.nodeComponentMap.insert(
430  {{block.port[p].positive, elements.dynamicElements.back()},
431  {block.port[p].negative, elements.dynamicElements.back()}});
432  }
433  }
434 };
435 
436 #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
ForceCausal::CausalData< T > forceCausal(const std::vector< T > &freq, const std::vector< std::complex< T > > &data)
Definition: ForceCausal.hpp:78
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
for k
Definition: interch.m:15
end if abs(real(dotprod))>rstoerst rstoerst
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.
A DTIR based model of an s-parameter block.
SParameterSequence< T > s
void addDCAnalysisStampTo(Stamp< T > &stamp, const Matrix< T > &solutionVector, size_t numCurrents) const
adds this component's DC stamp to the target stamp.
T beta_p(size_t p) const
T aWaveConvValue(size_t portIndex, const Matrix< T > &solutionMatrix, const size_t n, T sTimePoint, const T simulationTimestep, size_t sizeG_A) const
performs a linear interpolation and returns the a wave value for use in the convolution.
T V_p(size_t p, const Matrix< T > &solutionMatrix, const size_t n, T simulationTimestep, size_t sizeG_A) const
Determines the equivalent port voltage source by convolving the (historic) a wave values with the DTI...
T R_p(size_t p) const
void addStaticStampTo(Stamp< T > &stamp) const
Adds this component's static stamp to the target stamp.
void readInTouchstoneFile()
reads in the s-parameter data from a touchstone file. Currently it ignores the units of frequency,...
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.
static void addToElements(const std::string &line, CircuitElements< T > &elements, size_t &numNodes, size_t &numCurrents, size_t &numDCCurrents)
std::vector< SParameterPort< T > > port
std::string touchstoneFilePath
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.
a helper struct to store the information for the ports of an S-Parameter Block
T beta
a constant factor
T R
The equivalent resistance.
size_t negative
The negative index.
size_t current
The current index.
size_t positive
The positive index.
std::vector< T > s0
The start of each SParameterSequence.
a helper struct to store the DTIR sequence for the bloc
size_t & length(size_t a, size_t b)
const size_t offset(size_t a, size_t b) const
std::vector< T > _time
const T & data(size_t a, size_t b, size_t n) const
std::vector< T > _data
const T & time(size_t a, size_t b, size_t n) const
std::vector< SParamLengthOffset > sParamLengthOffset
T & data(size_t a, size_t b, size_t n)
T & time(size_t a, size_t b, size_t n)
const size_t length(size_t a, size_t b) const
size_t & offset(size_t a, size_t b)
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