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

openmc-dev / openmc / 13603648931

01 Mar 2025 10:03AM UTC coverage: 85.015% (-0.03%) from 85.042%
13603648931

Pull #3328

github

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

81 of 108 new or added lines in 6 files covered. (75.0%)

1 existing line in 1 file now uncovered.

51067 of 60068 relevant lines covered (85.02%)

32484760.55 hits per line

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

86.36
/src/ncrystal_load.cpp
1
#include "openmc/ncrystal_load.h"
2
#include "openmc/error.h"
3
#include <mutex>
4
#include <stdexcept>
5
#include <stdio.h>
6
#include <string>
7

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

20
namespace openmc {
21
namespace {
22

23
struct NCrystalConfig {
24
  std::string shlibpath;
25
  unsigned long intversion = 0;
26
  std::string symbol_namespace;
27
};
28

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

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

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

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

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

106
    std::string symbol("ncrystal");
1✔
107
    symbol += cfg.symbol_namespace;
1✔
108
    symbol += "_access_virtual_api";
1✔
109

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

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

130
  return result;
1✔
131
}
132

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

140
std::shared_ptr<const NCrystalAPI> load_ncrystal_api()
1✔
141
{
142
  auto& db = get_ncrystal_api_db();
1✔
143
  std::lock_guard<std::mutex> lock(db.mtx);
1✔
144
  if (!db.api) {
1✔
145
    void* raw_api = load_virtapi_raw(NCrystalAPI::interface_id, db);
1✔
146
    if (!raw_api)
1✔
NEW
147
      fatal_error("Problems loading NCrystal.");
×
148
    db.api = *reinterpret_cast<std::shared_ptr<const NCrystalAPI>*>(raw_api);
1✔
149
  }
150
  return db.api;
2✔
151
}
1✔
152
} // 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