• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

openmc-dev / openmc / 13637245381

03 Mar 2025 05:56PM UTC coverage: 85.015% (-0.03%) from 85.042%
13637245381

Pull #3328

github

web-flow
Merge 15aa74a51 into e2557bbe2
Pull Request #3328: NCrystal becomes runtime rather than buildtime dependency

79 of 106 new or added lines in 6 files covered. (74.53%)

1 existing line in 1 file now uncovered.

51065 of 60066 relevant lines covered (85.01%)

32743482.33 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

85.94
/src/ncrystal_load.cpp
1
#include "openmc/ncrystal_load.h"
2
#include "openmc/error.h"
3

4
#include <fmt/core.h>
5

6
#include <mutex>
7
#include <stdexcept>
8
#include <stdio.h>
9
#include <string>
10

11
#if !defined(NCLOAD_WINDOWS) && (defined(_WIN32) || defined(WIN32))
12
#define NCLOAD_WINDOWS
13
#endif
14
#ifdef NCLOAD_WINDOWS
15
#ifndef WIN32_LEAN_AND_MEAN
16
#define WIN32_LEAN_AND_MEAN
17
#endif
18
#include <windows.h>
19
#else
20
#include <dlfcn.h>
21
#endif
22

23
namespace openmc {
24
namespace {
25

26
struct NCrystalConfig {
27
  std::string shlibpath;
28
  unsigned long intversion = 0;
29
  std::string symbol_namespace;
30
};
31

32
NCrystalConfig query_ncrystal_config()
1✔
33
{
34
  char buffer[4096];
35
#ifdef NCLOAD_WINDOWS
36
  FILE* pipe = _popen("ncrystal-config --show "
37
                      "intversion shlibpath namespace",
38
    "r");
39
#else
40
  FILE* pipe = popen("ncrystal-config --show "
1✔
41
                     "intversion shlibpath namespace 2>/dev/null",
42
    "r");
43
#endif
44
  if (!pipe)
1✔
NEW
45
    return {}; // failure
×
46
  auto readLine = [pipe](std::string& tgt) -> bool {
3✔
47
    // Read line and discard trailing whitespace (including newline chars).
48
    char buffer[4096];
49
    if (fgets(buffer, sizeof(buffer), pipe) == NULL)
3✔
NEW
50
      return false;
×
51
    tgt = buffer;
3✔
52
    while (!tgt.empty() && std::isspace(tgt.back()))
6✔
53
      tgt.pop_back();
3✔
54
    return true;
3✔
55
  };
1✔
56
  auto parseIntVersion = [](const std::string& s) {
1✔
57
    char* str_end = nullptr;
1✔
58
    unsigned long v = std::strtoul(s.c_str(), &str_end, 10);
1✔
59
    return (v >= 2002000 && v < 999999999 && str_end == s.c_str() + s.size())
1✔
60
             ? v
2✔
61
             : 0;
1✔
62
  };
63

64
  NCrystalConfig res;
1✔
65
  bool all_ok(true);
1✔
66
  if (!readLine(res.shlibpath) ||
1✔
67
      !(res.intversion = parseIntVersion(res.shlibpath)) ||
1✔
68
      !readLine(res.shlibpath) || res.shlibpath.empty() ||
3✔
69
      !readLine(res.symbol_namespace)) {
1✔
NEW
70
    res.intversion = 0; // failure
×
71
  }
72

73
#ifdef NCLOAD_WINDOWS
74
  auto returnCode = _pclose(pipe);
75
#else
76
  auto returnCode = pclose(pipe);
1✔
77
#endif
78
  if (returnCode == 0 && res.intversion >= 2002000)
1✔
79
    return res;
1✔
NEW
80
  return {}; // failure
×
81
}
1✔
82

83
struct NCrystalAPIDB {
84
  std::mutex mtx;
85
  std::shared_ptr<const NCrystalAPI> api;
86
  typedef void* (*FctSignature)(int);
87
  FctSignature ncrystal_access_virtapi_fct = nullptr;
88
};
89

90
void* load_virtapi_raw(unsigned interface_id, NCrystalAPIDB& db)
1✔
91
{
92
  if (!db.ncrystal_access_virtapi_fct) {
1✔
93
    auto cfg = query_ncrystal_config();
1✔
94
    if (!(cfg.intversion >= 4001000)) {
1✔
95
      // This is the most likely error message people will see:
NEW
96
      fatal_error("Could not locate a functioning and recent enough"
×
97
                  " NCrystal installation (required since geometry"
98
                  " contains NCrystal materials).");
99
    }
100
#ifdef NCLOAD_WINDOWS
101
    auto handle = LoadLibrary(cfg.shlibpath.c_str());
102
#else
103
    dlerror(); // clear previous errors
1✔
104
    void* handle = dlopen(cfg.shlibpath.c_str(), RTLD_LOCAL | RTLD_LAZY);
1✔
105
#endif
106
    if (!handle)
1✔
NEW
107
      fatal_error("Loading of the NCrystal library failed");
×
108

109
    std::string symbol =
110
      fmt::format("ncrystal{}_access_virtual_api", cfg.symbol_namespace);
1✔
111

112
#ifdef NCLOAD_WINDOWS
113
    FARPROC fproc;
114
    void* addr = (void*)(intptr_t)GetProcAddress(handle, symbol.c_str());
115
    if (!addr)
116
      fatal_error("GetProcAddress("
117
                  "ncrystal_access_virtual_api) failed");
118
#else
119
    dlerror(); // clear previous errors
1✔
120
    void* addr = dlsym(handle, symbol.c_str());
1✔
121
    if (!addr)
1✔
NEW
122
      fatal_error("dlsym(ncrystal_access_virtual_api) failed");
×
123
#endif
124
    db.ncrystal_access_virtapi_fct =
1✔
125
      reinterpret_cast<NCrystalAPIDB::FctSignature>(addr);
1✔
126
  }
1✔
127

128
  void* result = (*db.ncrystal_access_virtapi_fct)(interface_id);
1✔
129
  if (!result)
1✔
NEW
130
    fatal_error("NCrystal installation does not support required interface.");
×
131

132
  return result;
1✔
133
}
134

135
NCrystalAPIDB& get_ncrystal_api_db()
1✔
136
{
137
  static NCrystalAPIDB db;
1✔
138
  return db;
1✔
139
}
140
} // namespace
141

142
std::shared_ptr<const NCrystalAPI> load_ncrystal_api()
1✔
143
{
144
  auto& db = get_ncrystal_api_db();
1✔
145
  std::lock_guard<std::mutex> lock(db.mtx);
1✔
146
  if (!db.api) {
1✔
147
    void* raw_api = load_virtapi_raw(NCrystalAPI::interface_id, db);
1✔
148
    if (!raw_api)
1✔
NEW
149
      fatal_error("Problems loading NCrystal.");
×
150
    db.api = *reinterpret_cast<std::shared_ptr<const NCrystalAPI>*>(raw_api);
1✔
151
  }
152
  return db.api;
2✔
153
}
1✔
154
} // namespace openmc
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc