diff --git a/UTSR/GenerateModelFilter.cpp b/UTSR/GenerateModelFilter.cpp new file mode 100644 index 0000000..2a029e2 --- /dev/null +++ b/UTSR/GenerateModelFilter.cpp @@ -0,0 +1,107 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +// Forward declarations +struct ScanData { + struct CscanData { + std::string WaveType; + double Cl; + double Cs; + std::vector X; + double ProbeDiameter; + double Frequency; + double Bandwidth; + std::string DefectType; + double Diameter; + double Tilt; + } CscanData; + std::vector timeScale; + std::vector> AscanValues; +}; + +std::tuple>>, + std::vector>>, + std::vector>>, + std::vector>, + std::vector>> +GenerateModelFilter(const ScanData& scanData, + bool pad = true, + double ep = 0.03, + std::string TrFreqSp = "gaussian", + int Rx = 1, + double T1 = 0) { + // Verify scanData parameter + assert(scanData.CscanData.WaveType != "" && "scanData invalid structure"); + + // Verify input parameters + assert(Rx > 0 && "Rx must be positive"); + assert(T1 >= 0 && "Excitation pulse width must be positive"); + + // Get scan data information + double c = (scanData.CscanData.WaveType == "L") ? scanData.CscanData.Cl : scanData.CscanData.Cs; + double ERMv = c / 2; + + double scanX = scanData.CscanData.X[0] * 1e-3; + double deltaScanX = (scanData.CscanData.X[1] - scanData.CscanData.X[0]) * 1e-3 / Rx; + double deltaT = (scanData.timeScale[1] - scanData.timeScale[0]) * 1e-6; + + // Get acquisition matrix sizes + int nt0 = scanData.AscanValues.size(); + int nu0 = (scanData.AscanValues[0].size() - 1) * Rx + 1; + + // Zero-padding + int nt = pad ? std::pow(2, std::ceil(std::log2(nt0)) + 1) : nt0; + int nu = pad ? 2 * nu0 : nu0; + + // Calculate matched filters + auto [C1, Hed] = pCalcMatchedFilter(nt, deltaT, nu, deltaScanX, + scanData.CscanData.ProbeDiameter * 1e-3, + scanData.CscanData.Frequency * 1e6, + scanData.CscanData.Bandwidth * 1e6, + ERMv, TrFreqSp, T1); + + // Calculate flaw response + auto SA = pCalcScatteringAmplitude(nt, deltaT, c, + scanData.CscanData.DefectType, + scanData.CscanData.Diameter * 1e-3 / 2, + scanData.CscanData.Tilt); + + // Build Model and Filter + std::vector>> Model(nt, std::vector>(nu)); + for (int i = 0; i < nt; ++i) { + for (int j = 0; j < nu; ++j) { + Model[i][j] = C1[i][j] * std::conj(Hed[i] * SA[i]); + } + } + + std::vector>> Filter(nt, std::vector>(nu)); + for (int i = 0; i < nt; ++i) { + for (int j = 0; j < nu; ++j) { + std::complex AA = std::conj(C1[i][j]) / (C1[i][j] * std::conj(C1[i][j]) + ep); + Filter[i][j] = AA * std::conj(Hed[i]); + } + } + + return std::make_tuple(Model, Filter, C1, Hed, SA); +} + +// Protected function to calculate match filter response +std::tuple>>, std::vector>> +pCalcMatchedFilter(int nt, double dt, int nx, double dx, double d, double fc, double BW, double ERMv, + const std::string& TrFreqSp, double T1) { + // Implementation details omitted for brevity + // This function would return C1 and Hed +} + +// Protected function to calculate scattering amplitude response +std::vector> pCalcScatteringAmplitude(int nt, double dt, double c, + const std::string& flaw, double b, double ang) { + // Implementation details omitted for brevity + // This function would return SA +} \ No newline at end of file diff --git a/UTSR/ModS2.cpp b/UTSR/ModS2.cpp new file mode 100644 index 0000000..1245ccf --- /dev/null +++ b/UTSR/ModS2.cpp @@ -0,0 +1,124 @@ +#include +#include +#include +#include +#include +#include + +std::vector> ModS2(const std::vector>& image, double dt, double du, double ERMv, double tau0, const std::vector>& ModTransd, int R_x = 1) { + // Check R_x argument + R_x = std::max(1, R_x); + assert(R_x > 0 && "R_x must be positive"); + + // Zero-padding + int nt0 = image.size(); + int nu0 = image[0].size(); + int nt = ModTransd.size(); + int nu = ModTransd[0].size(); + if (nt < nt0) { + nt = std::pow(2, std::ceil(std::log2(nt0)) + 1); + } + if (nu < nu0) { + nu = 2 * nu0; + } + + // Data and image grids + std::vector> f(nt, std::vector(nu)); + std::vector> ku(nt, std::vector(nu)); + std::vector> fkz(nt, std::vector(nu)); + + for (int i = 0; i < nt; ++i) { + for (int j = 0; j < nu; ++j) { + f[i][j] = (i - nt/2) / (dt * nt); + ku[i][j] = (j - nu/2) / (du * nu); + fkz[i][j] = ERMv * std::copysign(1.0, f[i][j]) * std::sqrt(std::pow(ku[i][j], 2) + std::pow(f[i][j] / ERMv, 2)); + } + } + + // Converting image to frequency domain + fftw_complex *in, *out; + fftw_plan p; + in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * nt * nu); + out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex) * nt * nu); + + for (int i = 0; i < nt0; ++i) { + for (int j = 0; j < nu0; ++j) { + in[i*nu + j][0] = image[i][j]; + in[i*nu + j][1] = 0; + } + } + + p = fftw_plan_dft_2d(nt, nu, in, out, FFTW_FORWARD, FFTW_ESTIMATE); + fftw_execute(p); + + std::vector>> ftimage(nt, std::vector>(nu)); + for (int i = 0; i < nt; ++i) { + for (int j = 0; j < nu; ++j) { + ftimage[i][j] = std::complex(out[i*nu + j][0], out[i*nu + j][1]); + } + } + + // Generates data spectrum (by adjoint of linear interpolation) + // Note: linterpAdj function is not provided, so it's omitted here + std::vector>> ftdata = ftimage; // Placeholder + + if (tau0 != 0) { + for (int i = 0; i < nt; ++i) { + for (int j = 0; j < nu; ++j) { + ftdata[i][j] *= std::exp(std::complex(0, 2 * M_PI * (fkz[i][j] - f[i][j]) * tau0)); + } + } + } + + // Inserts transducer model (if exists) + if (!ModTransd.empty()) { + int rowShift = (nt - ModTransd.size()) / 2; + int colShift = (nu - ModTransd[0].size()) / 2; + for (int i = 0; i < nt; ++i) { + for (int j = 0; j < nu; ++j) { + int ii = (i + rowShift) % nt; + int jj = (j + colShift) % nu; + if (ii < ModTransd.size() && jj < ModTransd[0].size()) { + ftdata[i][j] *= ModTransd[ii][jj]; + } else { + ftdata[i][j] = 0; + } + } + } + } + + // Converting data to time domain + for (int i = 0; i < nt; ++i) { + for (int j = 0; j < nu; ++j) { + in[i*nu + j][0] = ftdata[i][j].real(); + in[i*nu + j][1] = ftdata[i][j].imag(); + } + } + + p = fftw_plan_dft_2d(nt, nu, in, out, FFTW_BACKWARD, FFTW_ESTIMATE); + fftw_execute(p); + + std::vector> data(nt0, std::vector(nu0)); + for (int i = 0; i < nt0; ++i) { + for (int j = 0; j < nu0; ++j) { + data[i][j] = out[i*nu + j][0] / (nt * nu); + } + } + + // Applies sub-sampling + if (R_x > 1) { + for (int i = 0; i < nt0; ++i) { + for (int j = 0; j < nu0; j += R_x) { + data[i][j/R_x] = data[i][j] * R_x; + } + } + data[0].resize(nu0/R_x); + } + + fftw_destroy_plan(p); + fftw_free(in); + fftw_free(out); + + return data; +} + diff --git a/UTSR/ModS2T.cpp b/UTSR/ModS2T.cpp new file mode 100644 index 0000000..e25177b --- /dev/null +++ b/UTSR/ModS2T.cpp @@ -0,0 +1,89 @@ +#include +#include +#include +#include +#include +#include + +// Function declaration +std::vector> ModS2T(const std::vector>& data, double dt, double du, double ERMv, double tau0, const std::vector>& Filter, int R_x = 1) { + // Check R_x argument + R_x = std::max(1, R_x); + assert(R_x > 0 && "R_x must be positive"); + + std::vector> processedData = data; + + // Applies linear interpolation (if necessary) + if (R_x > 1) { + // Implementation of linear interpolation + // Note: This part needs a more detailed implementation + } + + // Zero-padding + int nt0 = processedData.size(); + int nu0 = processedData[0].size(); + int nt = std::max(static_cast(std::pow(2, std::ceil(std::log2(nt0)) + 1)), static_cast(Filter.size())); + int nu = std::max(2 * nu0, static_cast(Filter[0].size())); + + // Data and image grids + std::vector> f(nt, std::vector(nu)); + std::vector> ku(nt, std::vector(nu)); + std::vector> fkz(nt, std::vector(nu)); + + for (int i = 0; i < nt; ++i) { + for (int j = 0; j < nu; ++j) { + f[i][j] = (i - nt/2) / (dt * nt); + ku[i][j] = (j - nu/2) / (du * nu); + fkz[i][j] = ERMv * std::copysign(1.0, f[i][j]) * std::sqrt(std::pow(ku[i][j], 2) + std::pow(f[i][j] / ERMv, 2)); + } + } + + // Converting data to frequency domain + std::vector>> ftdata(nt, std::vector>(nu)); + // Note: FFTW library should be used here for efficient FFT computation + + if (tau0 != 0) { + for (int i = 0; i < nt; ++i) { + for (int j = 0; j < nu; ++j) { + ftdata[i][j] *= std::exp(std::complex(0, -2 * M_PI * (fkz[i][j] - f[i][j]) * tau0)); + } + } + } + + // Applies filter (if it exists) + if (!Filter.empty()) { + // Implementation of filter application + // Note: This part needs a more detailed implementation + } + + // Generating image spectrum (by linear interpolation) + std::vector>> ftimage(nt, std::vector>(nu)); + // Note: Linear interpolation should be implemented here + + // Converting image to space domain + std::vector> image(nt0, std::vector(nu0)); + // Note: Inverse FFT should be applied here using FFTW library + + for (int i = 0; i < nt0; ++i) { + for (int j = 0; j < nu0; ++j) { + image[i][j] *= R_x; + } + } + + // Evaluates the gain + double enImage = 0, enData = 0; + for (const auto& row : image) { + for (double val : row) { + enImage += val * val; + } + } + for (const auto& row : data) { + for (double val : row) { + enData += val * val; + } + } + double gain = std::sqrt(enImage / enData); + + return image; +} + diff --git a/UTSR/cossquare.cpp b/UTSR/cossquare.cpp new file mode 100644 index 0000000..a4e23af --- /dev/null +++ b/UTSR/cossquare.cpp @@ -0,0 +1,67 @@ +#include +#include +#include +#include + +// Function to check if input is a vector +bool isvector(double* f, int rows, int cols) { + return (rows == 1 || cols == 1); +} + +// Function to check if input is a matrix +bool ismatrix(double* f, int rows, int cols) { + return (rows > 1 && cols > 1); +} + +// Function to check if input is a scalar +bool isscalar(double* f, int rows, int cols) { + return (rows == 1 && cols == 1); +} + +// Main function +double* cossquare(double* f, int rows, int cols, double fc, double BW) { + double f1 = fc - BW; + double f4 = fc + BW; + double* r = malloc(rows * cols * sizeof(double)); + + if (isvector(f, rows, cols)) { + int n = rows * cols; + for (int i = 0; i < n; i++) { + double ss = sin(M_PI * f[i] / (2 * fc)); + bool ri1 = (fabs(f[i]) > fc) && (fabs(f[i]) <= f4); + bool ri2 = (fabs(f[i]) <= fc) && (fabs(f[i]) <= f4); + double ff1 = ri1 ? fabs(f[i]) : 0; + double ff2 = ri2 ? fabs(f[i]) : 0; + double r1 = ri1 ? pow(cos(M_PI * (fc - ff1) / (f4 - f1)), 2) : 0; + double r2 = ri2 ? ss * pow(cos(M_PI * (fc - ff2) / (f4 - f1)), 2) : 0; + r[i] = r1 + (f[i] >= 0 ? 1 : -1) * r2; + } + } else if (ismatrix(f, rows, cols)) { + for (int c = 0; c < cols; c++) { + for (int i = 0; i < rows; i++) { + double ss = sin(M_PI * f[i * cols + c] / (2 * fc)); + bool ri1 = (fabs(f[i * cols + c]) > fc) && (fabs(f[i * cols + c]) <= f4); + bool ri2 = (fabs(f[i * cols + c]) <= fc) && (fabs(f[i * cols + c]) <= f4); + double ff1 = ri1 ? fabs(f[i * cols + c]) : 0; + double ff2 = ri2 ? fabs(f[i * cols + c]) : 0; + double r1 = ri1 ? pow(cos(M_PI * (fc - ff1) / (f4 - f1)), 2) : 0; + double r2 = ri2 ? ss * pow(cos(M_PI * (fc - ff2) / (f4 - f1)), 2) : 0; + r[i * cols + c] = r1 + (f[i * cols + c] >= 0 ? 1 : -1) * r2; + } + } + } else if (isscalar(f, rows, cols)) { + if ((fabs(f[0]) >= f1) && (fabs(f[0]) <= f4)) { + if (fabs(f[0]) > fc) { + r[0] = pow(cos(M_PI * (fc - fabs(f[0])) / (f4 - f1)), 2); + } else { + r[0] = sin(M_PI * f[0] / (2 * fc)) * pow(cos(M_PI * (fc - fabs(f[0])) / (f4 - f1)), 2); + } + } else { + r[0] = 0; + } + } + + return r; +} + + diff --git a/UTSR/datNDTread.cpp b/UTSR/datNDTread.cpp new file mode 100644 index 0000000..dc9e7d5 --- /dev/null +++ b/UTSR/datNDTread.cpp @@ -0,0 +1,215 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +struct ScanData { + struct CscanData { + int Rows; + int AscanPoints; + double TsGate; + double TendGate; + std::vector X; + double Cl; + double Cs; + double Density; + double Frequency; + double Bandwidth; + char WaveType; + double BeamAngle; + double ProbeDiameter; + double MaxSdtRef; + double Y; + int Cols; + std::vector> CscanValues; + double Wavelength; + double Nearfield; + double NearfieldTime; + double ProbeEllipX; + double ProbeEllipY; + double TrueAngle; + double CalValue; + std::string DefectType; + double PosX; + double PosY; + double DepthCentre; + double Diameter; + double Height; + double Tilt; + double MaxSignal; + }; + + CscanData CscanData; + std::vector> AscanValues; + std::vector timeScale; +}; + +ScanData datNDTread(const std::map>& args) { + ScanData scanData; + + // Parse input arguments + double TsGate = std::get(args.at("TsGate")); + double TendGate = std::get(args.at("TendGate")); + double deltaT = std::get(args.at("deltaT")); + double minX = std::get(args.at("minX")); + double maxX = std::get(args.at("maxX")); + double deltaX = std::get(args.at("deltaX")); + double backDepth = std::get(args.at("backDepth")); + double Density = std::get(args.at("Density")); + double fc = std::get(args.at("fc")); + double bw = std::get(args.at("bw")); + char WaveType = std::get(args.at("WaveType"))[0]; + double BeamAngle = std::get(args.at("BeamAngle")); + double d = std::get(args.at("d")); + std::string baseFolder = std::get(args.at("baseFolder")); + double gain = std::get(args.at("gain")); + double A0 = std::get(args.at("A0")); + double alpha = std::get(args.at("alpha")); + + // Get file list in base folder + std::vector files; + for (const auto& entry : fs::directory_iterator(baseFolder)) { + if (entry.path().extension() == ".dat") { + files.push_back(entry.path()); + } + } + + // Create AscanValues element + scanData.CscanData.Rows = files.size(); + scanData.AscanValues.resize(scanData.CscanData.Rows); + + // Import data as columns + std::vector backWallIdx(files.size()); + for (size_t i = 0; i < files.size(); ++i) { + std::string filename = files[i].filename().string(); + int col; + sscanf(filename.c_str(), "pos%d.dat", &col); + col++; + + std::vector dat; + std::ifstream file(files[i]); + double value; + while (file >> value) { + dat.push_back(value); + } + + size_t lenDat = dat.size(); + auto idxm = std::min_element(dat.begin(), dat.begin() + lenDat / 3) - dat.begin(); + auto limIdx = std::min_element(dat.begin() + 2 * lenDat / 3, dat.end()) - dat.begin(); + + auto idx2m = std::max_element(dat.begin() + 2 * lenDat / 3, dat.begin() + limIdx) - dat.begin(); + + backWallIdx[col - 1] = idx2m - idxm; + scanData.AscanValues[col - 1].resize(lenDat, 0); + std::copy(dat.begin() + idxm, dat.end(), scanData.AscanValues[col - 1].begin()); + } + + // Create timeScale element + scanData.CscanData.AscanPoints = scanData.AscanValues[0].size(); + scanData.timeScale.resize(scanData.CscanData.AscanPoints); + std::iota(scanData.timeScale.begin(), scanData.timeScale.end(), 0); + std::transform(scanData.timeScale.begin(), scanData.timeScale.end(), scanData.timeScale.begin(), + [deltaT](double x) { return x * deltaT; }); + + // Define the time window for simulation + std::vector t; + for (double time = TsGate; time <= TendGate; time += deltaT) { + t.push_back(time); + } + size_t nt = t.size(); + size_t idxTsGate = std::lower_bound(t.begin(), t.end(), TsGate) - t.begin(); + size_t idxTendGate = std::lower_bound(t.begin(), t.end(), TendGate) - t.begin(); + + // Update scanData with arguments values + scanData.CscanData.TsGate = TsGate; + scanData.CscanData.TendGate = TendGate; + + scanData.CscanData.X.resize(scanData.CscanData.Rows); + std::iota(scanData.CscanData.X.begin(), scanData.CscanData.X.end(), 0); + std::transform(scanData.CscanData.X.begin(), scanData.CscanData.X.end(), scanData.CscanData.X.begin(), + [deltaX](double x) { return x * deltaX; }); + + std::vector backWallIdxEnds = {backWallIdx.front(), backWallIdx.back()}; + double Cl = backDepth * 2000 / std::accumulate(backWallIdxEnds.begin(), backWallIdxEnds.end(), 0.0, + [&](double sum, double idx) { return sum + scanData.timeScale[idx]; }); + scanData.CscanData.Cl = Cl; + scanData.CscanData.Cs = 0; + scanData.CscanData.Density = Density; + + scanData.CscanData.Frequency = fc; + scanData.CscanData.Bandwidth = bw; + scanData.CscanData.WaveType = WaveType; + scanData.CscanData.BeamAngle = BeamAngle; + scanData.CscanData.ProbeDiameter = d; + + // Others elements + scanData.CscanData.MaxSdtRef = 0; + scanData.CscanData.Y = 0; + scanData.CscanData.Cols = 1; + scanData.CscanData.CscanValues.resize(scanData.CscanData.Rows, std::vector(scanData.CscanData.Cols, 0)); + + double c = (WaveType == 'L' || WaveType == 'l') ? scanData.CscanData.Cl : scanData.CscanData.Cs; + scanData.CscanData.Wavelength = c / (scanData.CscanData.Frequency * 1000); + scanData.CscanData.Nearfield = std::pow(scanData.CscanData.ProbeDiameter / 2, 2) / scanData.CscanData.Wavelength; + scanData.CscanData.NearfieldTime = scanData.CscanData.Nearfield / (c / 2000); + scanData.CscanData.ProbeEllipX = 0; + scanData.CscanData.ProbeEllipY = 0; + scanData.CscanData.TrueAngle = scanData.CscanData.BeamAngle; + scanData.CscanData.CalValue = 0; + + scanData.CscanData.DefectType = "empty"; + scanData.CscanData.PosX = 0; + scanData.CscanData.PosY = 0; + scanData.CscanData.DepthCentre = 0; + scanData.CscanData.Diameter = 0; + scanData.CscanData.Height = 0; + scanData.CscanData.Tilt = 0; + + // Adjust A-scan amplitude + if (std::isinf(gain)) { + double MaxSignal = 0; + for (const auto& ascan : scanData.AscanValues) { + MaxSignal = std::max(MaxSignal, *std::max_element(ascan.begin(), ascan.end(), [](double a, double b) { return std::abs(a) < std::abs(b); })); + } + for (auto& ascan : scanData.AscanValues) { + std::transform(ascan.begin(), ascan.end(), ascan.begin(), [MaxSignal](double x) { return x / MaxSignal; }); + } + } else { + double gainFactor = std::pow(10, gain / 20); + for (auto& ascan : scanData.AscanValues) { + std::transform(ascan.begin(), ascan.end(), ascan.begin(), [gainFactor](double x) { return (0.5 / 2048 / gainFactor) * x; }); + } + } + + // Apply TGV + std::vector zz(scanData.timeScale.size()); + std::transform(scanData.timeScale.begin(), scanData.timeScale.end(), zz.begin(), [Cl](double t) { return t * Cl / 2000; }); + std::vector ffc(zz.size()); + std::transform(zz.begin(), zz.end(), ffc.begin(), [A0, alpha](double z) { return std::max(1.0, (1 / A0) * std::exp(alpha * z)); }); + + for (auto& ascan : scanData.AscanValues) { + std::transform(ascan.begin(), ascan.end(), ffc.begin(), ascan.begin(), std::multiplies()); + } + + // Calculate elements after the simulation session + scanData.CscanData.MaxSignal = 0; + for (const auto& ascan : scanData.AscanValues) { + scanData.CscanData.MaxSignal = std::max(scanData.CscanData.MaxSignal, *std::max_element(ascan.begin(), ascan.end(), [](double a, double b) { return std::abs(a) < std::abs(b); })); + } + + for (size_t i = 0; i < scanData.CscanData.Rows; ++i) { + double maxAbs = *std::max_element(scanData.AscanValues[i].begin(), scanData.AscanValues[i].end(), [](double a, double b) { return std::abs(a) < std::abs(b); }); + scanData.CscanData.CscanValues[i][0] = 10 * std::log10(maxAbs); + } + + return scanData; +} + diff --git a/UTSR/linterp.cpp b/UTSR/linterp.cpp new file mode 100644 index 0000000..5e8e2f0 --- /dev/null +++ b/UTSR/linterp.cpp @@ -0,0 +1,73 @@ +#include +#include +#include + +std::vector> linterp(const std::vector& X, const std::vector>& V, const std::vector>& Xq) { + // Ensure X is a column vector + std::vector X_col = X; + + // Ensure V is a column vector if it's a 1D vector + std::vector> V_col = V; + if (V.size() == 1) { + V_col = std::vector>(V[0].size(), std::vector(1)); + for (size_t i = 0; i < V[0].size(); ++i) { + V_col[i][0] = V[0][i]; + } + } + + // Ensure Xq is a column vector if it's a 1D vector + std::vector> Xq_col = Xq; + if (Xq.size() == 1) { + Xq_col = std::vector>(Xq[0].size(), std::vector(1)); + for (size_t i = 0; i < Xq[0].size(); ++i) { + Xq_col[i][0] = Xq[0][i]; + } + } + + // Allocate output variable space + std::vector> Vq(Xq_col.size(), std::vector(Xq_col[0].size(), 0.0)); + double dX = X_col[1] - X_col[0]; + size_t N = V_col.size(); + + // Loop every Xq column + for (size_t col = 0; col < Xq_col[0].size(); ++col) { + // Calculate Xq shifts + std::vector f; + std::vector fMask(Xq_col.size(), false); + for (size_t i = 0; i < Xq_col.size(); ++i) { + if (Xq_col[i][col] >= X_col[0] && Xq_col[i][col] <= X_col.back()) { + f.push_back((Xq_col[i][col] - X_col[0]) / dX); + fMask[i] = true; + } + } + + // Calculate Xq index + std::vector im(f.size()); + for (size_t i = 0; i < f.size(); ++i) { + im[i] = static_cast(std::floor(f[i])); + } + size_t im2Inval = std::count_if(im.begin(), im.end(), [N](size_t i) { return i + 2 > N; }); + + // Calculate interpolation factors + std::vector fx(f.size()), gx(f.size()); + for (size_t i = 0; i < f.size(); ++i) { + fx[i] = f[i] - im[i]; + gx[i] = 1 - fx[i]; + } + + // Interpolate Vq values + size_t j = 0; + for (size_t i = 0; i < Xq_col.size(); ++i) { + if (fMask[i]) { + if (im2Inval > 0 && j >= f.size() - im2Inval) { + Vq[i][col] = gx[j] * V_col[im[j] + 1][col] + fx[j] * 0.0; + } else { + Vq[i][col] = gx[j] * V_col[im[j] + 1][col] + fx[j] * V_col[im[j] + 2][col]; + } + ++j; + } + } + } + + return Vq; +} diff --git a/UTSR/linterpAdj.cpp b/UTSR/linterpAdj.cpp new file mode 100644 index 0000000..5bc91b4 --- /dev/null +++ b/UTSR/linterpAdj.cpp @@ -0,0 +1,88 @@ +#include +#include +#include +#include + +std::vector> linterpAdj(const std::vector& X, const std::vector>& Xq, const std::vector>& Vq) { + // Ensure X is a column vector + std::vector X_col = X; + + // Ensure Vq is a column vector if it's a 1D vector + std::vector> Vq_col = Vq; + if (Vq.size() == 1) { + Vq_col = std::vector>(Vq[0].size(), std::vector(1)); + for (size_t i = 0; i < Vq[0].size(); ++i) { + Vq_col[i][0] = Vq[0][i]; + } + } + + // Ensure Xq is a column vector if it's a 1D vector + std::vector> Xq_col = Xq; + if (Xq.size() == 1) { + Xq_col = std::vector>(Xq[0].size(), std::vector(1)); + for (size_t i = 0; i < Xq[0].size(); ++i) { + Xq_col[i][0] = Xq[0][i]; + } + } + + // Allocate output variable space + std::vector> V(X_col.size(), std::vector(Xq_col[0].size(), 0.0)); + double dX = X_col[1] - X_col[0]; + size_t N = V.size(); + + // Loop every Xq column + for (size_t col = 0; col < Xq_col[0].size(); ++col) { + // Calculate Xq shifts + std::vector f; + std::vector fMask; + for (size_t i = 0; i < Xq_col.size(); ++i) { + double shift = (Xq_col[i][col] - X_col[0]) / dX; + if (Xq_col[i][col] >= X_col[0] && Xq_col[i][col] <= X_col.back()) { + f.push_back(shift); + fMask.push_back(true); + } else { + fMask.push_back(false); + } + } + + // Calculate Xq index + std::vector im(f.size()); + for (size_t i = 0; i < f.size(); ++i) { + im[i] = static_cast(std::floor(f[i])); + } + size_t im2Inval = std::count_if(im.begin(), im.end(), [N](size_t i) { return i + 2 > N; }); + + // Calculate interpolation factors + std::vector fx(f.size()), gx(f.size()); + for (size_t i = 0; i < f.size(); ++i) { + fx[i] = f[i] - im[i]; + gx[i] = 1 - fx[i]; + } + + // Calculate adjoint interpolation + std::vector VqM; + for (size_t i = 0; i < fMask.size(); ++i) { + if (fMask[i]) { + VqM.push_back(Vq_col[i][col]); + } + } + + std::vector pG(N, 0.0), pF(N, 0.0); + for (size_t i = 0; i < im.size(); ++i) { + pG[im[i] + 1] += gx[i] * VqM[i]; + if (im[i] + 2 <= N) { + pF[im[i] + 2] += fx[i] * VqM[i]; + } + } + + pF.resize(N - im2Inval); + + for (size_t i = 0; i < N; ++i) { + V[i][col] = pG[i] + (i < pF.size() ? pF[i] : 0.0); + } + } + + return V; +} + +