From 0e818d315d0f72152fb55dc690603b75dda47e7d Mon Sep 17 00:00:00 2001 From: Carl Philipp Klemm <philipp@uvos.xyz> Date: Fri, 17 Nov 2023 14:30:09 +0100 Subject: [PATCH] basicmath: add support for extrapolateing spectra --- CMakeLists.txt | 3 -- basicmath.cpp | 76 ++++++++++++++++++++++++++++++++++++++++ eisgenerator/basicmath.h | 2 +- eisgenerator/eistype.h | 1 + eistype.cpp | 55 ++++++++++++++++++----------- main.cpp | 7 +++- options.h | 32 ++++++++--------- 7 files changed, 135 insertions(+), 41 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 2d3c8de..9fec7ab 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -112,9 +112,6 @@ install(TARGETS ${PROJECT_NAME} DESTINATION lib) install(TARGETS ${PROJECT_NAME}_static DESTINATION lib) install(TARGETS ${PROJECT_NAME}_obj FILE_SET header_set DESTINATION include/${PROJECT_NAME}) install(FILES ${CMAKE_CURRENT_BINARY_DIR}/pkgconfig/libeisgenerator.pc DESTINATION lib/pkgconfig) -install(FILES eisgenerator_plot DESTINATION bin PERMISSIONS WORLD_EXECUTE WORLD_READ -OWNER_READ OWNER_WRITE OWNER_EXECUTE -GROUP_READ GROUP_EXECUTE) install(FILES kissplotcsv DESTINATION bin PERMISSIONS WORLD_EXECUTE WORLD_READ OWNER_READ OWNER_WRITE OWNER_EXECUTE GROUP_READ GROUP_EXECUTE ) diff --git a/basicmath.cpp b/basicmath.cpp index d451c8b..84db4b3 100644 --- a/basicmath.cpp +++ b/basicmath.cpp @@ -4,6 +4,7 @@ #include <limits> #include "eistype.h" +#include "log.h" static size_t gradIndex(size_t dataSize, size_t inputIndex) { @@ -226,3 +227,78 @@ bool eis::fvalueEq(fvalue a, fvalue b, unsigned int ulp) fvalue epsilon = std::numeric_limits<fvalue>::epsilon()*std::fabs(a+b)*ulp; return a - epsilon < b && a + epsilon > b; } + +static std::pair<std::vector<eis::DataPoint>::const_iterator, std::vector<eis::DataPoint>::const_iterator> +getLrClosest(const eis::DataPoint& dp, std::vector<eis::DataPoint>::const_iterator start, std::vector<eis::DataPoint>::const_iterator end) +{ + + std::vector<eis::DataPoint>::const_iterator left = end; + fvalue distLeft = std::numeric_limits<fvalue>::max(); + std::vector<eis::DataPoint>::const_iterator right = end; + fvalue distRight = std::numeric_limits<fvalue>::max(); + + for(std::vector<eis::DataPoint>::const_iterator it = start; it != end; it++) + { + if(eis::fvalueEq(it->omega, dp.omega)) + return {it, it}; + fvalue dist = it->omega-dp.omega; + bool sign = std::signbit(dist); + dist = std::abs(dist); + + if(sign && (left == end || dist < distLeft)) + { + distLeft = dist; + left = it; + } + else if(!sign && (right == end || dist < distRight)) + { + distRight = dist; + right = it; + } + } + return {left, right}; +} + +static eis::DataPoint linearInterpolatePoint(fvalue omega, const eis::DataPoint& left, const eis::DataPoint& right) +{ + assert(left.omega <= omega); + assert(right.omega >= omega); + + fvalue omegaDif = right.omega - left.omega; + std::complex<fvalue> sloap = (right.im - left.im)/omegaDif; + return eis::DataPoint(left.im+sloap*(omega - left.omega), omega); +} + +std::vector<eis::DataPoint> eis::fitToFrequencies(std::vector<fvalue> omegas, const std::vector<eis::DataPoint>& data) +{ + std::vector<eis::DataPoint> out; + out.reserve(omegas.size()); + for(fvalue omega : omegas) + out.push_back(eis::DataPoint({0,0}, omega)); + + eis::Log(eis::Log::DEBUG)<<__func__<<':'; + + for(eis::DataPoint& dp : out) + { + auto lr = getLrClosest(dp, data.begin(), data.end()); + + eis::Log(eis::Log::DEBUG)<<"\tValue for "<<dp.omega; + if(lr.first != data.end()) + eis::Log(eis::Log::DEBUG)<<"\tLeft "<<lr.first->omega<<','<<lr.first->im; + if(lr.second != data.end()) + eis::Log(eis::Log::DEBUG)<<"\tRight "<<lr.second->omega<<','<<lr.second->im; + + if(lr.first == lr.second) + dp.im = lr.first->im; + else if(lr.first != data.end() && lr.second != data.end()) + dp = linearInterpolatePoint(dp.omega, *lr.first, *lr.second); + else if(lr.first != data.end() && lr.second == data.end()) + dp.im = lr.first->im; + else if(lr.first == data.end() && lr.second != data.end()) + dp.im = lr.second->im; + else + assert(false); + } + + return out; +} diff --git a/eisgenerator/basicmath.h b/eisgenerator/basicmath.h index a14b4ab..f1c7381 100644 --- a/eisgenerator/basicmath.h +++ b/eisgenerator/basicmath.h @@ -1,6 +1,5 @@ #pragma once #include <vector> -#include "model.h" #include "eistype.h" namespace eis @@ -19,5 +18,6 @@ namespace eis void noise(std::vector<eis::DataPoint>& data, double amplitude, bool relative); void removeDuplicates(std::vector<eis::DataPoint>& data); bool fvalueEq(fvalue a, fvalue b, unsigned int ulp = 4); + std::vector<eis::DataPoint> fitToFrequencies(std::vector<fvalue> omegas, const std::vector<eis::DataPoint>& data); } diff --git a/eisgenerator/eistype.h b/eisgenerator/eistype.h index 09aa1ae..78ed81b 100644 --- a/eisgenerator/eistype.h +++ b/eisgenerator/eistype.h @@ -122,6 +122,7 @@ public: bool isSane() const; std::vector<fvalue> getRangeVector() const; + static Range fromString(std::string str, size_t count); static std::vector<Range> rangesFromParamString(const std::string& paramStr, size_t count); }; diff --git a/eistype.cpp b/eistype.cpp index e1c97dd..a17f4c3 100644 --- a/eistype.cpp +++ b/eistype.cpp @@ -36,6 +36,40 @@ std::vector<fvalue> eis::Range::getRangeVector() const return out; } +eis::Range eis::Range::fromString(std::string str, size_t count) +{ + bool log = false; + std::vector<std::string> tokens = tokenize(str, '~'); + eis::Range out; + + if(str.back() == 'l' || str.back() == 'L') + { + log = true; + str.pop_back(); + } + + try + { + if(tokens.size() == 1) + { + out = Range(std::stod(tokens[0]), std::stod(tokens[0]), 1, log); + } + else + { + out = Range(std::stod(tokens[0]), std::stod(tokens[1]), count, log); + if(tokens.size() > 2) + throw std::invalid_argument(""); + } + + } + catch(const std::invalid_argument& ia) + { + throw std::invalid_argument("invalid parameter \""+ str + "\""); + } + + return out; +} + std::vector<Range> eis::Range::rangesFromParamString(const std::string& paramStr, size_t count) { std::vector<std::string> tokens = tokenize(paramStr, ','); @@ -43,29 +77,10 @@ std::vector<Range> eis::Range::rangesFromParamString(const std::string& paramStr std::vector<Range> ranges(tokens.size()); for(size_t i = 0; i < tokens.size(); ++i) { - bool log = false; std::string& token = tokens[i]; - std::vector<std::string> subTokens = tokenize(token, '~'); - - if(token.back() == 'l' || token.back() == 'L') - { - log = true; - token.pop_back(); - } - try { - if(subTokens.size() == 1) - { - ranges[i] = Range(std::stod(subTokens[0]), std::stod(subTokens[0]), 1, log); - } - else - { - ranges[i] = Range(std::stod(subTokens[0]), std::stod(subTokens[1]), count, log); - if(subTokens.size() > 2) - throw std::invalid_argument(""); - } - + ranges[i] = fromString(token, count); } catch(const std::invalid_argument& ia) { diff --git a/main.cpp b/main.cpp index 8667dca..6c058da 100644 --- a/main.cpp +++ b/main.cpp @@ -1,4 +1,3 @@ -#include <eisgenerator/eistype.h> #include <iostream> #include <complex> #include <chrono> @@ -60,6 +59,12 @@ static void runSweep(const Config& config, eis::Model& model) eis::Log(eis::Log::INFO)<<"normalized results:"; eis::normalize(results); } + else if(config.extrapolate) + { + std::vector<fvalue> exrapolateOmegas = config.extrapolateRange.getRangeVector(); + results = eis::fitToFrequencies(exrapolateOmegas, results); + eis::Log(eis::Log::INFO)<<"extrapolated results:"; + } else { eis::Log(eis::Log::INFO)<<"results:"; diff --git a/options.h b/options.h index f866547..4db314e 100644 --- a/options.h +++ b/options.h @@ -17,7 +17,8 @@ static struct argp_option options[] = {"quiet", 'q', 0, 0, "only output data" }, {"param", 's', "[COUNT]", 0, "parameter sweep steps" }, {"model", 'm', "[STRING]", 0, "set model string" }, - {"omega", 'o', "[START-END]", 0, "set omega range" }, + {"omega", 'o', "[START~END]", 0, "set omega range" }, + {"extrapolate", 'a', "[START~END]", 0, "extrapolate a spectra simulated on the omega range to the one given here" }, {"omegasteps", 'c', "[COUNT]", 0, "set omega range steps" }, {"linear", 'l', 0, 0, "use linear instead of logarithmic steps" }, {"normalize", 'n', 0, 0, "normalize values" }, @@ -62,6 +63,8 @@ struct Config int mode = MODE_NORMAL; size_t paramSteps = 10; eis::Range omegaRange; + bool extrapolate = false; + eis::Range extrapolateRange; bool normalize = false; bool reduce = false; bool hertz = false; @@ -152,26 +155,23 @@ parse_opt (int key, char *arg, struct argp_state *state) break; case 'o': { - std::vector<std::string> tokens = tokenize(std::string(arg), '-'); - if(tokens.size() != 2) + try + { + config->omegaRange = eis::Range::fromString(std::string(arg), config->omegaRange.count); + } + catch(const std::invalid_argument& ia) { argp_usage(state); - break; } + break; + } + case 'a': + { try { - double start = std::stod(tokens[0]); - double end = std::stod(tokens[1]); - if(start < end) - { - config->omegaRange.start = start; - config->omegaRange.end = end; - } - else - { - config->omegaRange.start = end; - config->omegaRange.end = start; - } + config->extrapolateRange = eis::Range::fromString(std::string(arg), config->omegaRange.count); + config->extrapolateRange.log = config->omegaRange.log; + config->extrapolate = true; } catch(const std::invalid_argument& ia) { -- GitLab