Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-10-09 08:30:26

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #include "ActsPlugins/Mille/MillePedeSolver.hpp"
0010 
0011 #include "Acts/Utilities/Logger.hpp"
0012 #include "Acts/Utilities/Result.hpp"
0013 #include "ActsPlugins/Mille/MillePedeError.hpp"
0014 
0015 #include <filesystem>
0016 #include <fstream>
0017 #include <sstream>
0018 #include <string>
0019 #include <tuple>
0020 
0021 #include "detail/RunSolverProcess.hpp"
0022 
0023 namespace {
0024 
0025 ActsPlugins::MillePedeSolver::ExitStatus interpretExit(int theExitCode) {
0026   using enum ActsPlugins::MillePedeSolver::ExitStatus;
0027   // implementation follows
0028   // https://millepede.pages.desy.de/millepede-ii/exit_code_page.html
0029   if (theExitCode < 0) {
0030     return NotFinishedOrCrashed;
0031   } else if (theExitCode == 0) {
0032     return NominalExit;
0033   } else if (theExitCode == 1) {
0034     return TolerableWarnings;
0035   } else if (theExitCode <= 4) {
0036     return SeriousWarnings;
0037   } else if (theExitCode == 5) {
0038     return NoSolution;
0039   } else {
0040     return Aborted;
0041   }
0042 }
0043 
0044 std::tuple<int, ActsPlugins::MillePedeSolver::ExitStatus, std::string>
0045 readDetailedExit(const std::filesystem::path& mpend) {
0046   std::ifstream exitCodeFile(mpend);  // open the Pede exit code file
0047   std::string statusMessage = "";
0048   int retCode = 0;
0049   // read the pede exit code from the file.
0050   exitCodeFile >> retCode;
0051   // some acrobatics to get a nice formatting of the remaining text
0052   std::stringstream remainingCont;
0053   remainingCont << exitCodeFile.rdbuf();
0054   statusMessage = remainingCont.str();
0055   statusMessage = statusMessage.substr(statusMessage.find_first_not_of(' '));
0056   statusMessage =
0057       statusMessage.substr(0, statusMessage.find_last_not_of(" \n") + 1);
0058   exitCodeFile.close();
0059 
0060   ActsPlugins::MillePedeSolver::ExitStatus status = interpretExit(retCode);
0061   return {retCode, status, statusMessage};
0062 }
0063 
0064 std::filesystem::path copyIfRequested(
0065     const std::filesystem::path& originalLoc,
0066     const std::optional<std::filesystem::path>& userLoc,
0067     const Acts::Logger& logger) {
0068   if (!std::filesystem::exists(originalLoc)) {
0069     return "";
0070   }
0071   if (!userLoc.has_value()) {
0072     return originalLoc;
0073   }
0074   if (!std::filesystem::copy_file(
0075           originalLoc, *userLoc,
0076           std::filesystem::copy_options::overwrite_existing)) {
0077     ACTS_WARNING(" Failed to copy output file " << originalLoc << " to "
0078                                                 << *userLoc);
0079     return originalLoc;
0080   }
0081   return *userLoc;
0082 }
0083 
0084 }  // namespace
0085 
0086 namespace ActsPlugins {
0087 
0088 Acts::Result<MillePedeSolver::Result> MillePedeSolver::solve(
0089     const Config& cfg) const {
0090   ACTS_INFO("=== Proceeding to run Millepede-II alignment fit ===");
0091 
0092   // determine where the user wishes to run
0093   std::filesystem::path workDir =
0094       cfg.workDir.value_or(std::filesystem::current_path());
0095 
0096   // create the work dir if it does not exist already
0097   if (!std::filesystem::exists(workDir)) {
0098     std::filesystem::create_directories(workDir);
0099   }
0100 
0101   ACTS_DEBUG("Will run the alignment fit in folder '"
0102              << std::filesystem::absolute(workDir) << "'");
0103 
0104   // make sure the steering file exists (or is empty)
0105   std::vector<std::string> mpArgs = cfg.extraOpts;
0106 
0107   if (std::filesystem::path steerPath = cfg.steeringFile; !steerPath.empty()) {
0108     if (steerPath.is_relative()) {
0109       steerPath = std::filesystem::current_path() / steerPath;
0110     }
0111     if (!std::filesystem::exists(steerPath)) {
0112       ACTS_ERROR("Steering file " << steerPath
0113                                   << " does not seem to exist - aborting!");
0114       return Acts::Result<Result>::failure(MillePedeError::SteeringNotFound);
0115     }
0116     mpArgs.push_back(steerPath);
0117   }
0118 
0119   ACTS_INFO(" Calling pede, this may take a while depending on problem size");
0120   // now run the fit
0121   detail::MpSolverStatus pedeProcessStatus = detail::runSolverProcess(
0122       "pede", mpArgs, workDir, logger(), cfg.redirectStdout);
0123 
0124   if (pedeProcessStatus == detail::MpSolverStatus::ProgNotFound) {
0125     ACTS_ERROR("Pede executable could not be found, aborting!");
0126     return Acts::Result<Result>::failure(MillePedeError::InstallationNotFound);
0127   }
0128   if (pedeProcessStatus == detail::MpSolverStatus::FailedWorkDir) {
0129     ACTS_ERROR("Could not navigate to the run directory `" << workDir
0130                                                            << "`, aborting!");
0131     return Acts::Result<Result>::failure(MillePedeError::SolverCrash);
0132   } else if (pedeProcessStatus != detail::MpSolverStatus::OK) {
0133     ACTS_ERROR("Pede invocation failed. Did not run alignment.");
0134     return Acts::Result<Result>::failure(MillePedeError::SolverCrash);
0135   }
0136 
0137   /// now check the detailed exit code - pede speaks fortranese, so we need to
0138   /// read this from a file
0139 
0140   // step 1: Ensure the file is actually there
0141   std::filesystem::path mpExit =
0142       workDir / std::filesystem::path("millepede.end");
0143   if (!std::filesystem::exists(mpExit)) {
0144     ACTS_ERROR(
0145         "Failed to find the Pede exit code file. The alignment has likely "
0146         "failed.");
0147     return Acts::Result<Result>::failure(MillePedeError::SolverCrash);
0148   }
0149 
0150   // step 2: Read and interpret the exit info file
0151   auto [exitCode, exitStatus, exitMessage] = readDetailedExit(mpExit);
0152 
0153   // step 3: Tell the user what happened
0154 
0155   if (exitStatus == ExitStatus::Aborted) {
0156     ACTS_ERROR("Pede Aborted due to errors:\n   "
0157                << exitMessage << "\n"
0158                << "Please check the millepede log files in " << workDir
0159                << " for more information.");
0160     return Acts::Result<Result>::failure(MillePedeError::InvalidSolution);
0161   }
0162 
0163   else if (exitStatus == ExitStatus::NoSolution) {
0164     ACTS_WARNING("Pede did not find a solution:\n   "
0165                  << exitMessage << "\n"
0166                  << "Please check the millepede log files in " << workDir
0167                  << " for more information.");
0168   }
0169 
0170   else if (exitStatus == ExitStatus::SeriousWarnings) {
0171     ACTS_WARNING("Pede exited with severe warnings:\n   "
0172                  << exitMessage << "\n"
0173                  << "You should check the millepede log files in " << workDir
0174                  << " for more information.");
0175   }
0176 
0177   else if (exitStatus == ExitStatus::TolerableWarnings) {
0178     ACTS_INFO("Pede exited with tolerable warnings:\n   "
0179               << exitMessage << "\n"
0180               << "You can check the millepede log files in " << workDir
0181               << " for more information.");
0182   } else {
0183     ACTS_INFO("Pede exited nominally:\n   "
0184               << exitMessage << "\n"
0185               << "You can check the millepede log files in " << workDir
0186               << " for more information.");
0187   }
0188 
0189   // step 4: Relocate output if configured by the user.
0190 
0191   auto outputResultFile =
0192       copyIfRequested(workDir / "millepede.res", cfg.resFileName, logger());
0193   auto outputLogFile =
0194       copyIfRequested(workDir / "millepede.log", cfg.logFileName, logger());
0195   auto outputHisFile =
0196       copyIfRequested(workDir / "millepede.his", cfg.histoFileName, logger());
0197   auto outputEvFile =
0198       copyIfRequested(workDir / "millepede.eve", cfg.evFileName, logger());
0199 
0200   // the one file we **always** expect to exist is the result file.
0201   // The others may not exist depending on the user steering options.
0202   if (outputResultFile.empty()) {
0203     ACTS_ERROR("Failed to find Millepede result file expected at "
0204                << workDir / "millepede.res");
0205     return Acts::Result<Result>::failure(MillePedeError::SolutionNotReadable);
0206   }
0207 
0208   ACTS_INFO("=== Congratulations, the alignment finished! ===");
0209 
0210   return Result{
0211       .exitCode = exitCode,
0212       .exitStatus = exitStatus,
0213       .exitMessage = exitMessage,
0214       .resultsFile = outputResultFile,  // file containing parameter results
0215       .logFile = outputLogFile,         // log file
0216       .histoFile = outputHisFile,       // file with validation histograms
0217       .evFile = outputEvFile            // file with eigenvectors
0218   };
0219 }
0220 
0221 }  // namespace ActsPlugins