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

openmc-dev / openmc / 22504841590

27 Feb 2026 09:39PM UTC coverage: 81.508% (-0.3%) from 81.826%
22504841590

Pull #3830

github

web-flow
Merge f607ed78f into b3788f11e
Pull Request #3830: Parallelize sampling external sources and threadsafe rejection counters

17485 of 25188 branches covered (69.42%)

Branch coverage included in aggregate %.

51 of 64 new or added lines in 4 files covered. (79.69%)

842 existing lines in 45 files now uncovered.

57731 of 67093 relevant lines covered (86.05%)

44980271.24 hits per line

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

89.53
/include/openmc/random_ray/parallel_map.h
1
#ifndef OPENMC_RANDOM_RAY_PARALLEL_HASH_MAP_H
2
#define OPENMC_RANDOM_RAY_PARALLEL_HASH_MAP_H
3

4
#include "openmc/openmp_interface.h"
5

6
#include <memory>
7
#include <unordered_map>
8

9
namespace openmc {
10

11
/*
12
 * The ParallelMap class allows for threadsafe access to a map-like data
13
 * structure. It is implemented as a hash table with a fixed number of buckets,
14
 * each of which contains a mutex lock and an unordered_map. The class provides
15
 * a subset of the functionality of std::unordered_map. Users must first lock
16
 * the object (using the key) before accessing or modifying the map. The object
17
 * is locked by bucket, allowing for multiple threads to manipulate different
18
 * keys simultaneously, though sometimes threads will need to wait if keys
19
 * happen to be in the same bucket. The ParallelMap will generate pointers to
20
 * hold values, rather than direct storage of values, so as to allow for
21
 * pointers to values to remain valid even after the lock has been released
22
 * (though locking of those values is then left to the user). Iterators to the
23
 * class are provided but are not threadsafe, and are meant to be used only in a
24
 * serial context (e.g., to dump the contents of the map to another data
25
 * structure).
26
 */
27

28
template<typename KeyType, typename ValueType, typename HashFunctor>
29
class ParallelMap {
762✔
30

31
  //----------------------------------------------------------------------------
32
  // Helper structs and classes
33

34
  struct Bucket {
915,000✔
35
    OpenMPMutex lock_;
36
    std::unordered_map<KeyType, std::unique_ptr<ValueType>, HashFunctor> map_;
37
  };
38

39
  // The iterator yields a pair: (const KeyType&, ValueType&)
40
  class iterator {
41
  public:
42
    using iterator_category = std::forward_iterator_tag;
43
    using value_type = std::pair<const KeyType&, ValueType&>;
44
    using difference_type = std::ptrdiff_t;
45
    using pointer = void; // Not providing pointer semantics.
46
    using reference = value_type;
47

48
    iterator(std::vector<Bucket>* buckets, std::size_t bucket_index,
31,264✔
49
      typename std::unordered_map<KeyType, std::unique_ptr<ValueType>,
50
        HashFunctor>::iterator inner_it)
51
      : buckets_(buckets), bucket_index_(bucket_index), inner_it_(inner_it)
31,264✔
52
    {
53
      // Advance to the first valid element if necessary.
54
      advance_to_valid();
15,632✔
55
    }
56

57
    // Dereference returns a pair of (key, value).
58
    reference operator*() const
2,366,891✔
59
    {
60
      return {inner_it_->first, *inner_it_->second};
2,366,891✔
61
    }
62

63
    iterator& operator++()
2,366,891✔
64
    {
65
      ++inner_it_;
2,366,891✔
66
      advance_to_valid();
2,366,891✔
67
      return *this;
2,366,891✔
68
    }
69

70
    iterator operator++(int)
71
    {
72
      iterator tmp = *this;
73
      ++(*this);
74
      return tmp;
75
    }
76

77
    bool operator==(const iterator& other) const
2,382,523✔
78
    {
79
      // Two iterators are equal if they refer to the same bucket vector and are
80
      // both at end, or if they have the same bucket index and inner iterator.
81
      return buckets_ == other.buckets_ &&
4,765,046✔
82
             bucket_index_ == other.bucket_index_ &&
2,382,523!
83
             (bucket_index_ == buckets_->size() ||
15,632!
UNCOV
84
               inner_it_ == other.inner_it_);
×
85
    }
86

87
    bool operator!=(const iterator& other) const { return !(*this == other); }
2,382,523✔
88

89
  private:
90
    // Helper function: if we are at the end of the current bucket, advance to
91
    // the next non-empty bucket.
92
    void advance_to_valid()
2,398,155✔
93
    {
94
      while (bucket_index_ < buckets_->size() &&
20,428,310✔
95
             inner_it_ == (*buckets_)[bucket_index_].map_.end()) {
17,998,891✔
96
        ++bucket_index_;
15,632,000✔
97
        if (bucket_index_ < buckets_->size())
15,632,000✔
98
          inner_it_ = (*buckets_)[bucket_index_].map_.begin();
15,616,368✔
99
      }
100
    }
2,398,155✔
101

102
    std::vector<Bucket>* buckets_;
103
    std::size_t bucket_index_;
104
    typename std::unordered_map<KeyType, std::unique_ptr<ValueType>,
105
      HashFunctor>::iterator inner_it_;
106
  };
107

108
public:
109
  //----------------------------------------------------------------------------
110
  // Constructor
111
  ParallelMap(int n_buckets = 1000) : buckets_(n_buckets) {}
762✔
112

113
  //----------------------------------------------------------------------------
114
  // Public Methods
115
  void lock(const KeyType& key)
23,941,989✔
116
  {
117
    Bucket& bucket = get_bucket(key);
23,941,989✔
118
    bucket.lock_.lock();
23,941,989✔
119
  }
23,941,989✔
120

121
  void unlock(const KeyType& key)
23,941,989✔
122
  {
123
    Bucket& bucket = get_bucket(key);
23,941,989✔
124
    bucket.lock_.unlock();
23,941,989✔
125
  }
23,941,989✔
126

127
  void clear()
15,632✔
128
  {
129
    for (auto& bucket : buckets_) {
15,647,632✔
130
      bucket.map_.clear();
15,632,000✔
131
    }
132
  }
15,632✔
133

134
  bool contains(const KeyType& key)
43,888,469✔
135
  {
136
    Bucket& bucket = get_bucket(key);
43,888,469✔
137
    // C++20
138
    // return bucket.map_.contains(key);
139
    return bucket.map_.find(key) != bucket.map_.end();
43,888,469✔
140
  }
141

142
  ValueType& operator[](const KeyType& key)
43,786,939✔
143
  {
144
    Bucket& bucket = get_bucket(key);
43,786,939✔
145
    return *bucket.map_[key].get();
43,786,939✔
146
  }
147

148
  ValueType* emplace(KeyType key, const ValueType& value)
2,366,891✔
149
  {
150
    Bucket& bucket = get_bucket(key);
2,366,891✔
151
    // Attempt to emplace the new element into the unordered_map within the
152
    auto result = bucket.map_.emplace(key, std::make_unique<ValueType>(value));
4,733,782✔
153
    auto it = result.first;
2,366,891✔
154
    return it->second.get();
2,366,891✔
155
  }
156

157
  // Return iterator to first element.
158
  iterator begin()
15,632✔
159
  {
160
    std::size_t bucket_index = 0;
15,632✔
161
    auto inner_it = buckets_.empty()
15,632!
162
                      ? typename std::unordered_map<KeyType,
163
                          std::unique_ptr<ValueType>, HashFunctor>::iterator()
164
                      : buckets_[0].map_.begin();
15,632✔
165
    return iterator(&buckets_, bucket_index, inner_it);
15,632✔
166
  }
167

168
  // Return iterator to one-past-last element.
169
  iterator end()
15,632✔
170
  {
171
    // End is signaled by bucket_index_ equal to buckets_.size()
172
    return iterator(&buckets_, buckets_.size(),
173
      typename std::unordered_map<KeyType, std::unique_ptr<ValueType>,
174
        HashFunctor>::iterator());
15,632✔
175
  }
176

177
private:
178
  //----------------------------------------------------------------------------
179
  // Private Methods
180
  Bucket& get_bucket(const KeyType& key)
137,926,277✔
181
  {
182
    return buckets_[hash(key) % buckets_.size()];
137,926,277✔
183
  }
184

185
  //----------------------------------------------------------------------------
186
  // Private Data Fields
187
  HashFunctor hash;
188
  vector<Bucket> buckets_;
189
};
190

191
} // namespace openmc
192

193
#endif // OPENMC_RANDOM_RAY_PARALLEL_HASH_MAP_H
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