1 #ifndef _SPARAMETERBLOCKVF_HPP_INC_
2 #define _SPARAMETERBLOCKVF_HPP_INC_
14 #include "MatlabEngine.hpp"
15 #include "MatlabDataArray.hpp"
22 std::vector<std::complex<T> >
pole;
29 std::vector<std::complex<T> >
mu_p;
31 std::vector<std::complex<T> >
nu_p;
38 std::complex<T>
mu = 0;
40 std::complex<T>
nu = 0;
43 std::vector<std::complex<T> >
x;
65 std::vector<std::complex<T> >
alpha;
68 std::complex<T>
R = 0;
70 std::vector<SParamVFDataFrom<T> >
from;
79 std::vector<SParameterPortVF<T> >
port;
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];
95 awave_p(c, solutionMatrix, currentSolutionIndex - 1, sizeG_A);
96 if (currentSolutionIndex > 1) {
98 currentSolutionIndex - 2,
107 const size_t currentSolutionIndex, T timestep,
const size_t sizeG_A)
const {
109 history_p(p, solutionMatrix, currentSolutionIndex, timestep, sizeG_A) *
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;
121 if (
port[p].positive) {
122 toRet += solutionMatrix(np, n);
125 if (
port[p].negative) {
126 toRet += -solutionMatrix(nn, n);
129 return (
toRet + solutionMatrix(sizeG_A + curr, n) *
z_ref) /
130 (2 * std::sqrt(
z_ref));
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;
141 if (
port[p].positive) {
142 toRet += solutionMatrix(np, n);
145 if (
port[p].negative) {
146 toRet += -solutionMatrix(nn, n);
149 return (
toRet - solutionMatrix(sizeG_A + curr, n) *
z_ref) /
150 (2 * std::sqrt(
z_ref));
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;
163 if (
port[p].positive != 0) {
164 stamp.
G(stamp.
sizeG_A + curr, np) += 1;
165 stamp.
G(np, stamp.
sizeG_A + curr) += 1;
168 if (
port[p].negative != 0) {
169 stamp.
G(stamp.
sizeG_A + curr, nn) += -1;
170 stamp.
G(nn, stamp.
sizeG_A + curr) += -1;
173 for (
size_t c = 0; c <
port.size(); c++) {
175 if (
port[c].positive != 0) {
178 1) += std::real(-
port[p].alpha[c]);
180 if (
port[c].negative != 0) {
182 port[c].negative - 1) += std::real(
port[p].alpha[c]);
186 1) += std::real(-
z_ref *
port[p].alpha[c]);
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;
199 0) +=
V_p(p, solutionMatrix, currentSolutionIndex,
200 simulationTimestep, stamp.
sizeG_A);
205 const size_t currentSolutionIndex, T timestep,
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] *
214 currentSolutionIndex,
216 port[p].from[c].mu_p[rho] *
218 currentSolutionIndex - 1,
221 port[p].from[c].x[rho] +=
port[p].from[c].nu_p[rho] *
223 currentSolutionIndex - 2,
230 if (
firstOrder && currentSolutionIndex >= 1) {
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;
240 port[p].R = 1.0 +
port[p].from[p].lambda +
port[p].from[p].remainder;
244 for (
size_t c = 0; c <
numPorts; c++) {
246 port[p].alpha[c] = 0;
248 port[p].alpha[c] =
port[p].from[c].lambda +
249 port[p].from[c].remainder;
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];
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];
277 port[p].from[c].nu_p[rho] = 0;
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];
303 port[p].from[c].mu_p[rho] = -(residue / pole) *
304 (-2.0 * (1.0 - ea) / (
a *
a) -
306 port[p].from[c].mu +=
port[p].from[c].mu_p[rho];
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];
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);
337 performVectorFit(std::string filePath,
size_t numPorts,
338 std::shared_ptr<matlab::engine::MATLABEngine> matlabEngine) {
339 matlab::data::ArrayFactory factory;
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);
346 assert(result[0].getDimensions()[0] ==
numPorts);
347 assert(result[0].getDimensions()[1] ==
numPorts);
349 z_ref =
static_cast<T
>(result[1][0]);
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]);
359 port[
a].from[
b].numPoles = structResult[
"poles"]
360 .getNumberOfElements();
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]);
377 port[
a].from[
b].remainder =
static_cast<std::complex<T>
>(
378 static_cast<matlab::data::TypedArray<std::complex<T>
> >(
379 structResult[
"remainder"])[0]);
386 std::ifstream file(filePath);
390 while (file.peek() ==
'#' || file.peek() ==
'!') {
391 std::getline(file, line);
397 std::stringstream polesLine;
398 std::stringstream residuesLine;
402 while (!file.eof()) {
406 for (
size_t c = 0; c <
numPorts; c++) {
407 file >> rval >> cval;
408 port[
a].from[c].remainder = std::complex(rval, cval);
417 std::getline(file, line);
418 polesLine = std::stringstream(line);
419 std::getline(file, line);
420 residuesLine = std::stringstream(line);
422 while (!polesLine.eof() && !residuesLine.eof()) {
423 polesLine >> rval >> cval;
424 port[
a].from[c].pole.emplace_back(rval, cval);
426 residuesLine >> rval >> cval;
427 port[
a].from[c].residue.emplace_back(rval, cval);
430 port[
a].from[c].numPoles =
port[
a].from[c].pole.size();
446 size_t numCurrents)
const {
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);
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;
465 std::complex<T> beta = 1.0 / (1.0 - xSum[p *
numPorts + p]);
470 if (
port[p].positive != 0) {
471 stamp.
G(stamp.
sizeG_A + curr, np) += 1;
472 stamp.
G(np, stamp.
sizeG_A + curr) += 1;
475 if (
port[p].negative != 0) {
476 stamp.
G(stamp.
sizeG_A + curr, nn) += -1;
477 stamp.
G(nn, stamp.
sizeG_A + curr) += -1;
480 for (
size_t c = 0; c <
port.size(); c++) {
482 if (
port[c].positive != 0) {
485 1) += std::real(-xSum[p *
numPorts + c] * beta);
487 if (
port[c].negative != 0) {
490 1) += std::real(xSum[p *
numPorts + c] * beta);
503 size_t & numNodes,
size_t & numCurrents,
size_t & numDCCurrents) {
505 std::regex SParameterBlockInitialRegex =
generateRegex(
"SV",
"n s",
true,
508 std::regex_search(line, matches, SParameterBlockInitialRegex);
515 size_t numPorts = std::stoul(matches.str(2));
517 block.
port = std::vector<SParameterPortVF<T> >(
numPorts);
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));
527 numNodes = std::max(numNodes, std::stoull(matches.str(1)));
528 numNodes = std::max(numNodes, std::stoull(matches.str(2)));
530 block.
port[p].current = ++numCurrents;
532 strIter = matches.suffix().first;
534 std::regex endOfLineRegex(R
"(^(.*)$)");
535 std::regex_search(strIter, line.cend(), matches, endOfLineRegex);
536 if (line[2] ==
'F') {
544 elements.dynamicElements.emplace_back(
546 for (
size_t p = 0; p <
numPorts; p++) {
548 {{block.
port[p].positive,
elements.dynamicElements.back()},
549 {block.
port[p].negative,
elements.dynamicElements.back()}});
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
Find a complex rational model for freq domain data for a
Number of poles to use in fit numPoles
a glorified container for the different types of components.
A template base class to define the fundamental things a component should define.
std::string designator
The designator as in the netlist for e.g.
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.