Program Listing for File NuMagSANSlib_InputFileInterpreter.h#

Return to documentation for file (src/NuMagSANSlib_InputFileInterpreter.h)

// File         : NuMagSANSlib_InputFileInterpreter.h
// Author       : Dr. Michael Philipp ADAMS
// Company      : University of Luxembourg
// Department   : Department of Physics and Materials Sciences
// Group        : NanoMagnetism Group
// Group Leader : Prof. Andreas Michels
// Version      : 28 October 2025
// OS           : Linux Ubuntu
// Language     : CUDA C++





#include <iostream>
#include <fstream>
#include <sstream>
#include <sys/stat.h>
#include <sys/types.h>
#include <math.h>
#include <string>
#include <vector>
#include <stdlib.h>
#include <time.h>
#include <cuda_runtime.h>
#include <cublas_v2.h>
#include <stdexcept>
#include <math.h>
#include <chrono>
#include <dirent.h>
#include <unistd.h>
#include <algorithm>
#include <cctype>

using namespace std;


// ##############################################################################################################################################
// ##############################################################################################################################################


struct OutFlag {
    bool Nuclear = false;
    bool Unpolarized = false;
    bool Polarized = false;
    bool NuclearMagnetic= false;
    bool SpinFlip= false;
    bool Chiral= false;
    bool PM_SpinFlip = false;
    bool MP_SpinFlip = false;
    bool PP_NonSpinFlip = false;
    bool MM_NonSpinFlip = false;
    bool P_SANSPOL = false;
    bool M_SANSPOL = false;
};

struct OutFlagQuant {
    OutFlag SANS2D{};
    OutFlag SANS1D{};
    OutFlag Corr2D{};
    OutFlag Corr1D{};
    OutFlag PairDist1D{};
};

struct PrefixEntry {
    const char* name;
    bool OutFlag::* member;
};

struct SuffixEntry {
    const char* name;
    OutFlag OutFlagQuant::* quantity;
};



struct InputFileData{

    string NucDataPath;

    string MagDataPath;

    string StructDataFilename;

    string SANSDataFoldername;

    string Fourier_Approach;

    bool Loop_Modus;

    int Loop_From;

    int Loop_To;

    string User_Selection;

    std::vector<int> User_Selection_IndexArray;

    // --- Discretization parameters ---

    int N_q;

    int N_theta;

    int N_r;

    int N_alpha;

    float q_max;

    float r_max;

    // --- Physical parameters ---

    float Scattering_Volume_V;

    float cell_nuclear_sld;

    float cell_magnetization;

    float cuboid_cell_size_x;

    float cuboid_cell_size_y;

    float cuboid_cell_size_z;

    // --- Rotation matrix ---

    float RotMat_alpha;

    float RotMat_beta;

    float RotMat[9];

    float XYZ_Unit_Factor;

    // --- Polarization vector ---

    float Polarization[3];

    // --- Activation flags ---

    bool NucData_activate_flag;

    bool MagData_activate_flag;

    bool StructData_activate_flag;

    bool ExcludeZeroMoments_flag;

    bool Check_InputFile_Flag;

    int k_max;

    bool AngularSpec_activate_flag;

    bool output_fourier_correlation_matrix_flag;

    /*
    string NucDataPath;
    string MagDataPath;
    string StructDataFilename;
    string SANSDataFoldername;
    string Fourier_Approach;

    bool Loop_Modus;
    int Loop_From;
    int Loop_To;

    string User_Selection;
    std::vector<int> User_Selection_IndexArray;

    int N_q;
    int N_theta;
    int N_r;
    int N_alpha;
    float q_max;
    float r_max;

    float Scattering_Volume_V;
    float cell_nuclear_sld;
    float cell_magnetization;
    float cuboid_cell_size_x;
    float cuboid_cell_size_y;
    float cuboid_cell_size_z;

    float RotMat_alpha;
    float RotMat_beta;
    float RotMat[9];
    float XYZ_Unit_Factor;

    float Polarization[3];

    bool NucData_activate_flag;
    bool MagData_activate_flag;
    bool StructData_activate_flag;
    bool ExcludeZeroMoments_flag;

    bool Check_InputFile_Flag;

    int k_max;
    bool AngularSpec_activate_flag;

    bool output_fourier_correlation_matrix_flag;
    */
    OutFlagQuant OutFlags{};

};


inline bool any_active(const OutFlag& f)
{
    return  f.Nuclear
         || f.Unpolarized
         || f.Polarized
         || f.NuclearMagnetic
         || f.SpinFlip
         || f.Chiral
         || f.PM_SpinFlip
         || f.MP_SpinFlip
         || f.PP_NonSpinFlip
         || f.MM_NonSpinFlip
         || f.P_SANSPOL
         || f.M_SANSPOL;
}



// zy-rotation matrix where alpha is the polar angle and beta is the azimuth angle
void Compute_RotMat(float alpha, float beta, float* RotMat){

    // ordering of the rotation matrix:
    // RotMat = [RotMat[0], RotMat[3], RotMat[6]; ...
    //           RotMat[1], RotMat[4], RotMat[7]; ...
    //           RotMat[2], RotMat[5], RotMat[8]]


     alpha = alpha * M_PI/180.0;
     beta = beta * M_PI/180.0;

     RotMat[0] = cos(alpha) * cos(beta);
     RotMat[1] = cos(alpha) * sin(beta);
     RotMat[2] = -sin(alpha);
     RotMat[3] = -sin(beta);
     RotMat[4] = cos(beta);
     RotMat[5] = 0;
     RotMat[6] = cos(beta) * sin(alpha);
     RotMat[7] = sin(alpha) * sin(beta);
     RotMat[8] = cos(alpha);

 }





static std::string trim(const std::string& s)
{
    size_t start = s.find_first_not_of(" \t\r\n");
    size_t end   = s.find_last_not_of(" \t\r\n");

    if (start == std::string::npos)
        return "";

    return s.substr(start, end - start + 1);
}


static bool try_extract_value(const std::string& line,
                              const std::string& property,
                              std::string& value)
{
    // Remove leading and trailing whitespace from the entire line
    std::string trimmed = trim(line);

    // If the line is shorter than the property name,
    // it cannot possibly match
    if (trimmed.size() < property.size())
        return false;

    // The line must start with the exact property name.
    // This ensures prefix matching but does NOT yet validate
    // token boundaries.
    if (trimmed.compare(0, property.size(), property) != 0)
        return false;

    // Position right after the property name
    size_t pos = property.size();

    // Skip any whitespace between the property and the '=' symbol
    while (pos < trimmed.size() &&
           std::isspace(static_cast<unsigned char>(trimmed[pos])))
        ++pos;

    // The next non-whitespace character MUST be '='.
    // This enforces an exact key match and prevents
    // matches like "q_max_extra".
    if (pos >= trimmed.size() || trimmed[pos] != '=')
        return false;

    // Find the terminating semicolon after '='
    size_t semi_pos = trimmed.find(';', pos);
    if (semi_pos == std::string::npos)
        return false;

    // Extract the substring between '=' and ';'
    // and trim surrounding whitespace
    value = trim(trimmed.substr(pos + 1,
                                semi_pos - pos - 1));

    return true;
}


void parseStringNoCout(const std::string& line,
                       const std::string& property,
                       std::string& variable,
                       bool& flag)
{
    if (!try_extract_value(line, property, variable))
        return;

    flag = true;
}

static std::vector<int> parse_int_list(const std::string& s)
{
    std::vector<int> result;

    std::string trimmed = trim(s);

    if (trimmed.front() != '{' || trimmed.back() != '}')
        throw std::runtime_error("Invalid User_Selection format");

    std::string content = trimmed.substr(1, trimmed.size() - 2);

    std::stringstream ss(content);
    std::string item;

    while (std::getline(ss, item, ',')) {
        result.push_back(std::stoi(trim(item)));
    }

    return result;
}

 // Interpreting User_Selection to integer array ##########################################
void User_Selection_To_Int_Array(int *Idx, int Number_of_Comma, string my_string){

     int Symbol_Idx[Number_of_Comma + 2];
     string Symbol_Start = "{";
     string Symbol_End = "}";
     string Symbol_Comma = ",";
     int Symbol_Counter = 0;
     for(int i = 0; i < my_string.size(); i++){
         if(my_string.at(i) == Symbol_Start.at(0) || my_string.at(i) == Symbol_End.at(0) || my_string.at(i) == Symbol_Comma.at(0)){
             Symbol_Idx[Symbol_Counter] = i;
             Symbol_Counter += 1;
         }
     }

     string Sub_String;
     int space_symbol_index;
     for(int i = 0; i <Number_of_Comma + 1; i++){
         Sub_String = my_string.substr(Symbol_Idx[i]+1, Symbol_Idx[i+1]-Symbol_Idx[i]-1);
         while(Sub_String.find(" ") <= Sub_String.size()){
             space_symbol_index = Sub_String.find(" ");
             Sub_String.erase(space_symbol_index, 1);
         }
         Idx[i] = stoi(Sub_String);
     }
}

// Find Number of Comma in string #########################################################
void Number_Of_Comma_In_String(int *N, string my_string){
     int String_Length = my_string.size();
     int Comma_Counter = 0;
     string Comma = ",";

     for(int i = 0; i < String_Length; i++){
         if(my_string.at(i) == Comma.at(0)){
             Comma_Counter += 1;
         }
     }
     *N = Comma_Counter;
}

// Pre-Parsing for the initialization of the LogFile System
void InitializeLogSystem(string filename){

    ifstream fin;
    fin.open(filename);
    string line;
    string string_to_be_interpreted;
    bool checkFlag = false;

    string SANSDataFoldername;

    while(getline(fin, line)){
        parseStringNoCout(line, "foldernameSANSData", SANSDataFoldername, checkFlag);
    }
    fin.close();

    if(checkFlag){
        mkdir((SANSDataFoldername + "/").c_str(), 0777);
            LogSystem::initLog(SANSDataFoldername + "/NuMagSANSlog.txt");
            //LogSystem::write("LogFile Initialized");
    }
    else {
            std::cerr << "Error: 'foldernameSANSData' not found in input file '"<< filename << "'. Logging not initialized.\n";
        std::cerr << "Aborting program.\n";
            std::exit(EXIT_FAILURE);
    }

}






// template for options that are parsed by the InputFileInterpreter
template<typename T>
struct Option {
    std::string key;
    T* target;
    bool required;
    bool found = false;
};

bool ReadCSV__Input_File_Interpreter(string filename, InputFileData*InputData){


    LogSystem::write("##########################################################################################");
    LogSystem::write("## Run - Input File Interpreter ##########################################################");
    LogSystem::write("##########################################################################################");


    ifstream fin(filename);
    if (!fin.is_open()) {
        LogSystem::write("Could not open input file.");
        return false;
    }

    // generalisation of the output options
    std::vector<PrefixEntry> prefixes = {
        {"Nuclear",        &OutFlag::Nuclear},
        {"Unpolarized",    &OutFlag::Unpolarized},
        {"Polarized",      &OutFlag::Polarized},
        {"NuclearMagnetic",&OutFlag::NuclearMagnetic},
        {"SpinFlip",       &OutFlag::SpinFlip},
        {"Chiral",         &OutFlag::Chiral},
        {"PM_SpinFlip",    &OutFlag::PM_SpinFlip},
        {"MP_SpinFlip",    &OutFlag::MP_SpinFlip},
        {"PP_NonSpinFlip", &OutFlag::PP_NonSpinFlip},
        {"MM_NonSpinFlip", &OutFlag::MM_NonSpinFlip},
        {"P_SANSPOL",      &OutFlag::P_SANSPOL},
        {"M_SANSPOL",      &OutFlag::M_SANSPOL}
    };

    std::vector<SuffixEntry> suffixes = {
        {"_2D",            &OutFlagQuant::SANS2D},
        {"_1D",            &OutFlagQuant::SANS1D},
        {"_Corr_2D",       &OutFlagQuant::Corr2D},
        {"_Corr_1D",       &OutFlagQuant::Corr1D},
        {"_PairDist_1D",   &OutFlagQuant::PairDist1D}
    };

    std::vector<Option<bool>> Output_options;
    for (auto& p : prefixes)
    {
        for (auto& s : suffixes)
        {
            std::string key = std::string(p.name) + s.name;
            Output_options.push_back({
                key,
                &( (InputData->OutFlags.*(s.quantity)).*(p.member) ),
                true
            });
        }
    }

    // bool options //////////////////////////////
    std::vector<Option<bool>> bool_options = {

        {"Loop_Modus", &InputData->Loop_Modus, true},
        {"NucData_activate", &InputData->NucData_activate_flag, true},
        {"MagData_activate", &InputData->MagData_activate_flag, true},
        {"StructData_activate", &InputData->StructData_activate_flag, true},
        {"Exclude_Zero_Moments", &InputData->ExcludeZeroMoments_flag, true},
        {"Angular_Spec", &InputData->AngularSpec_activate_flag, true},
        {"Fourier_Gamma", &InputData->output_fourier_correlation_matrix_flag, true}

    };

    // int options /////////////////////////////////
    std::vector<Option<int>> int_options = {

        {"Loop_From", &InputData->Loop_From, true},
        {"Loop_To", &InputData->Loop_To, true},
        {"Number_Of_q_Points", &InputData->N_q, true},
        {"Number_Of_theta_Points", &InputData->N_theta, true},
        {"Number_Of_r_Points", &InputData->N_r, true},
        {"Number_Of_alpha_Points", &InputData->N_alpha, true},
        {"k_max", &InputData->k_max, true}

    };

    // float options ///////////////////////////////
    std::vector<Option<float>> float_options = {

        {"q_max", &InputData->q_max, true},
        {"r_max", &InputData->r_max, true},
        {"Scattering_Volume_V", &InputData->Scattering_Volume_V, true},
        {"Cell_Nuclear_SLD", &InputData->cell_nuclear_sld, true},
        {"Cell_Magnetization", &InputData->cell_magnetization, true},
        {"Cuboid_Cell_Size_x", &InputData->cuboid_cell_size_x, true},
        {"Cuboid_Cell_Size_y", &InputData->cuboid_cell_size_y, true},
        {"Cuboid_Cell_Size_z", &InputData->cuboid_cell_size_z, true},
        {"RotMat_alpha", &InputData->RotMat_alpha, true},
        {"RotMat_beta", &InputData->RotMat_beta, true},
        {"XYZ_Unit_Factor", &InputData->XYZ_Unit_Factor, true},
        {"Polarization_x", &InputData->Polarization[0], true},
        {"Polarization_y", &InputData->Polarization[1], true},
        {"Polarization_z", &InputData->Polarization[2], true}

    };

    // string options
    std::vector<Option<std::string>> string_options = {

        {"NucDataPath", &InputData->NucDataPath, true},
        {"MagDataPath", &InputData->MagDataPath, true},
        {"StructDataFilename", &InputData->StructDataFilename, true},
        {"foldernameSANSData", &InputData->SANSDataFoldername, true},
        {"Fourier_Approach", &InputData->Fourier_Approach, true},
        {"User_Selection", &InputData->User_Selection, true}

    };


    std::string line;
    while(getline(fin, line)) {
        std::string value;

        for (auto& opt : Output_options) {
            if (try_extract_value(line, opt.key, value)) {
                if (opt.found) {
                    LogSystem::write("Duplicate definition of option: " + opt.key);
                    return false;
                }
                *opt.target = static_cast<bool>(std::stoi(value));
                opt.found = true;
                LogSystem::write(" -> " + opt.key + " : " + (*opt.target ? "true" : "false"));
            }
        }

        for (auto& opt : bool_options) {
            if (try_extract_value(line, opt.key, value)) {
                if (opt.found) {
                    LogSystem::write("Duplicate definition of option: " + opt.key);
                    return false;
                }
                *opt.target = static_cast<bool>(std::stoi(value));
                opt.found = true;
                LogSystem::write(" -> " + opt.key + " : " + (*opt.target ? "true" : "false"));
            }
        }

        for (auto& opt : int_options) {
            if (try_extract_value(line, opt.key, value)) {
                if (opt.found) {
                    LogSystem::write("Duplicate definition of option: " + opt.key);
                    return false;
                }
                *opt.target = std::stoi(value);
                opt.found = true;
                LogSystem::write(" -> " + opt.key + " : " + std::to_string(*opt.target));
            }
        }

        for (auto& opt : float_options) {
            if (try_extract_value(line, opt.key, value)) {
                if (opt.found) {
                    LogSystem::write("Duplicate definition of option: " + opt.key);
                    return false;
                }
                *opt.target = std::stof(value);
                opt.found = true;
                LogSystem::write(" -> " + opt.key + " : " + std::to_string(*opt.target));
            }
        }

        for (auto& opt : string_options) {
            if (try_extract_value(line, opt.key, value)) {
                if (opt.found) {
                    LogSystem::write("Duplicate definition of option: " + opt.key);
                    return false;
                }
                *opt.target = value;
                opt.found = true;
                LogSystem::write(" -> " + opt.key + " : " + *opt.target);
            }
        }

    }
    fin.close();




    for (auto& opt : string_options)
    if (opt.key == "User_Selection" && opt.found)
    {
        InputData->User_Selection_IndexArray =
            parse_int_list(InputData->User_Selection);

        LogSystem::write("Check UserSelection entries that are transferred to integer array:");

        for (size_t k = 0; k < InputData->User_Selection_IndexArray.size(); ++k)
        {
            LogSystem::write(
                " UserSelection: " +
                std::to_string(k) + " : " +
                std::to_string(InputData->User_Selection_IndexArray[k]));
        }
    }




    Compute_RotMat(InputData->RotMat_alpha, InputData->RotMat_beta, InputData->RotMat);
    LogSystem::write("Rotation Matrix: ");
    LogSystem::write(std::to_string(InputData->RotMat[0]) + " " + std::to_string(InputData->RotMat[3]) + " " + std::to_string(InputData->RotMat[6]));
    LogSystem::write(std::to_string(InputData->RotMat[1]) + " " + std::to_string(InputData->RotMat[4]) + " " + std::to_string(InputData->RotMat[7]));
    LogSystem::write(std::to_string(InputData->RotMat[2]) + " " + std::to_string(InputData->RotMat[5]) + " " + std::to_string(InputData->RotMat[8]));
    LogSystem::write("");
    LogSystem::write("");
    LogSystem::write("");




    // Check Polarization
    float P_norm = sqrt(pow(InputData->Polarization[0], 2) \
                      + pow(InputData->Polarization[1], 2) \
                      + pow(InputData->Polarization[2], 2));
    if(P_norm == 0){
        LogSystem::write("Error: Polarization magnitude is equal to zero!!");
    }
    if(P_norm != 0 && P_norm != 1){
        InputData->Polarization[0] = (InputData->Polarization[0])/P_norm;
        InputData->Polarization[1] = (InputData->Polarization[1])/P_norm;
        InputData->Polarization[2] = (InputData->Polarization[2])/P_norm;
        LogSystem::write("Polarization is automatically normalized to 1!");
    }



    LogSystem::write("##########################################################################################");
    LogSystem::write("## Stop - Input File Interpreter #########################################################");
    LogSystem::write("##########################################################################################");
    LogSystem::write("");


    bool ok = true;

    auto check_required = [&](auto& options)
    {
        for (auto& opt : options)
        {
            if (opt.required && !opt.found)
            {
                LogSystem::write("Missing required option: " + opt.key);
                ok = false;
            }
        }
    };

    check_required(Output_options);
    check_required(bool_options);
    check_required(int_options);
    check_required(float_options);
    check_required(string_options);

    if (!ok)
    {
        LogSystem::write(" ->-> Error in input file!");
    }

    return ok;

}