/*************************************************************************** PUserFcnBase.h Author: Andreas Suter e-mail: andreas.suter@psi.ch ***************************************************************************/ /*************************************************************************** * Copyright (C) 2007-2025 by Andreas Suter * * andreas.suter@psi.ch * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program; if not, write to the * * Free Software Foundation, Inc., * * 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * ***************************************************************************/ #ifndef _PUSERFCNBASE_H_ #define _PUSERFCNBASE_H_ #include #include "TObject.h" #include "TSAXParser.h" //-------------------------------------------------------------------------------------------- /** * \brief Abstract base class for user-defined theory functions in musrfit. * * PUserFcnBase enables extending musrfit with custom theory functions * beyond the 34 built-in functions. Users create derived classes implementing * specific physics models, compile them into shared libraries, and load them * dynamically at runtime via ROOT's plugin mechanism. * * \section userfcn_use_cases Use Cases * * User functions are valuable for: * - Novel relaxation mechanisms not in standard library * - Material-specific models (e.g., Skyrmion lattices, spin ice) * - Complex multi-component functions requiring custom logic * - Proprietary or experimental theory functions under development * - Functions requiring external libraries (GSL, CUDA, MKL, etc.) * - Performance-critical implementations with custom optimization * * \section userfcn_implementation Implementation Guide * * Step 1: Create header file (MyUserFcn.h) * \code{.cpp} * #ifndef MY_USER_FCN_H * #define MY_USER_FCN_H * * #include "PUserFcnBase.h" * * class TMyRelaxation : public PUserFcnBase { * public: * TMyRelaxation() {} * virtual ~TMyRelaxation() {} * * virtual Double_t operator()(Double_t t, const std::vector &par) const; * * ClassDef(TMyRelaxation, 1) * }; * * #endif * \endcode * * Step 2: Implement source file (MyUserFcn.cpp) * \code{.cpp} * #include "MyUserFcn.h" * #include * * ClassImp(TMyRelaxation) * * Double_t TMyRelaxation::operator()(Double_t t, const std::vector &par) const { * // par[0] = rate (lambda), par[1] = exponent (beta) * if (t < 0) return 1.0; * return exp(-pow(par[0] * t, par[1])); * } * \endcode * * Step 3: Create LinkDef file (MyUserFcnLinkDef.h) * \code{.cpp} * #ifdef __CINT__ * #pragma link off all globals; * #pragma link off all classes; * #pragma link off all functions; * * #pragma link C++ class TMyRelaxation+; * #endif * \endcode * * Step 4: Build shared library * \code{.sh} * rootcint -f MyUserFcnDict.cxx -c MyUserFcn.h MyUserFcnLinkDef.h * g++ -shared -fPIC -o libMyUserFcn.so MyUserFcn.cpp MyUserFcnDict.cxx \ * $(root-config --cflags --libs) -I$MUSRFIT/include * \endcode * * Step 5: Use in MSR file * \code * THEORY * asymmetry 1 * userFcn libMyUserFcn.so TMyRelaxation 2 3 (rate, exponent) * \endcode * * \section userfcn_global Global Part for Expensive Computations * * For functions requiring expensive one-time setup (lookup tables, matrix * decompositions, file loading), implement the global part interface: * * \code{.cpp} * class TMyComplexFcn : public PUserFcnBase { * private: * mutable void *fGlobal; // Pointer to global data * * public: * virtual Bool_t NeedGlobalPart() const { return true; } * * virtual void SetGlobalPart(std::vector &globalPart, UInt_t idx) { * if (idx < globalPart.size() && globalPart[idx] != nullptr) { * fGlobal = globalPart[idx]; // Reuse existing * } else { * fGlobal = new MyGlobalData(); // Create new * static_cast(fGlobal)->Initialize(); * if (idx < globalPart.size()) * globalPart[idx] = fGlobal; * else * globalPart.push_back(fGlobal); * } * } * * virtual Bool_t GlobalPartIsValid() const { * return fGlobal != nullptr; * } * * // ... operator() uses fGlobal for fast lookup * }; * \endcode * * \section userfcn_parameters Parameter Conventions * * In the MSR file THEORY block: * \code * userFcn libName.so ClassName param1 param2 ... paramN * \endcode * * Parameters can be: * - Direct numbers: \c 1, \c 2 → parameter indices from FITPARAMETER block * - Map references: \c map1, \c map2 → via RUN block map * - Function references: \c fun1, \c fun2 → evaluated FUNCTIONS * * Convention: The last parameter is typically a time shift. * * \see PTheory for how user functions are loaded and called * \see PUserFcn for a simple example implementation */ class PUserFcnBase : public TObject { public: /// Default constructor PUserFcnBase() {} /// Virtual destructor virtual ~PUserFcnBase() {} /** *

Indicates if this function requires global initialization. * *

Override to return true if your function needs expensive one-time * setup (e.g., calculating lookup tables, loading external data). * The global part is computed once and reused across fit iterations. * * @return true if global part needed, false otherwise (default: false) */ virtual Bool_t NeedGlobalPart() const { return false; } /** *

Sets up the global part of the user function. * *

Called once during initialization if NeedGlobalPart() returns true. * Use this to allocate and initialize shared data structures. * * @param globalPart Vector of global objects (one per run) * @param idx Index of this run's global object in the vector */ virtual void SetGlobalPart(std::vector &globalPart, UInt_t idx) {} /** *

Checks if the global part initialized correctly. * *

Override to validate your global data structure after SetGlobalPart(). * * @return true if global part is valid and ready, false otherwise (default: false) */ virtual Bool_t GlobalPartIsValid() const { return false; } /** *

Evaluates the user function at time t (pure virtual). * *

This is the core evaluation method called for each data point * during fitting. Must be implemented by derived classes. * *

Parameter convention: The last parameter is typically the * time shift. Earlier parameters are model-specific (rates, amplitudes, * exponents, etc.). * * @param t Time in microseconds (μs) * @param param Vector of function parameters (from MSR file + maps) * @return Function value at time t */ virtual Double_t operator()(Double_t t, const std::vector ¶m) const = 0; ClassDef(PUserFcnBase, 1) }; //-------------------------------------------------------------------------- /** *

XML file parser for user function configurations. * *

This function provides a replacement for TSAXParser::ParseFile with * better error handling for XML configuration files used by some user * functions to load parameters or settings. * * @param parser Pointer to TSAXParser object * @param fileName Path to XML file to parse * @return 0 on success, error code on failure */ Int_t parseXmlFile(TSAXParser*, const Char_t*); #endif // _PUSERFCNBASE_H_