BornAgain  1.19.79
Open-source research software to simulate and fit neutron and x-ray reflectometry and grazing-incidence small-angle scattering
ISimulation.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file Sim/Simulation/ISimulation.cpp
6 //! @brief Implements interface ISimulation.
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
17 #include "Base/Util/Assert.h"
18 #include "Base/Util/StringUtils.h"
26 #include <gsl/gsl_errno.h>
27 #include <iostream>
28 #include <thread>
29 
30 namespace {
31 
32 size_t indexStep(size_t total_size, size_t n_handlers)
33 {
34  ASSERT(total_size > 0);
35  ASSERT(n_handlers > 0);
36  size_t result = total_size / n_handlers;
37  return total_size % n_handlers ? ++result : result;
38 }
39 
40 size_t startIndex(size_t n_handlers, size_t current_handler, size_t n_elements)
41 {
42  const size_t handler_size = indexStep(n_elements, n_handlers);
43  const size_t start_index = current_handler * handler_size;
44  if (start_index >= n_elements)
45  return n_elements;
46  return start_index;
47 }
48 
49 size_t batchSize(size_t n_handlers, size_t current_handler, size_t n_elements)
50 {
51  const size_t handler_size = indexStep(n_elements, n_handlers);
52  const size_t start_index = current_handler * handler_size;
53  if (start_index >= n_elements)
54  return 0;
55  return std::min(handler_size, n_elements - start_index);
56 }
57 
58 void runComputations(const std::vector<std::unique_ptr<IComputation>>& computations)
59 {
60  ASSERT(!computations.empty());
61 
62  if (computations.size() == 1) { // Running computation in current thread
63  const auto& computation = computations.front();
64  computation->compute();
65  if (computation->isCompleted())
66  return;
67  std::string message = computation->errorMessage();
68  throw std::runtime_error("Error in runComputations: ISimulation has "
69  "terminated unexpectedly with following error: "
70  "message.\n"
71  + message);
72  }
73 
74  // Running computations in several threads.
75  // The number of threads is equal to the number of computations.
76 
77  std::vector<std::unique_ptr<std::thread>> threads;
78 
79  // Run simulations in n threads.
80  for (const auto& comp : computations)
81  threads.emplace_back(new std::thread([&comp]() { comp->compute(); }));
82 
83  // Wait for threads to complete.
84  for (auto& thread : threads)
85  thread->join();
86 
87  // Check successful completion.
88  std::vector<std::string> failure_messages;
89  for (const auto& comp : computations)
90  if (!comp->isCompleted())
91  failure_messages.push_back(comp->errorMessage());
92 
93  if (failure_messages.empty())
94  return;
95  throw std::runtime_error("Error in runComputations: "
96  "At least one simulation thread has terminated unexpectedly.\n"
97  "Messages: "
98  + BaseUtils::String::join(failure_messages, " --- "));
99 }
100 
101 } // namespace
102 
103 
104 // ************************************************************************************************
105 // class implementation
106 // ************************************************************************************************
107 
109  : m_sample(sample.clone())
110  , m_options(std::make_unique<SimulationOptions>())
111  , m_progress(std::make_unique<ProgressHandler>())
112 {
113 }
114 
115 ISimulation::~ISimulation() = default;
116 
118 {
119  ASSERT(m_options);
120  return *m_options;
121 }
122 
124 {
125  ASSERT(m_options);
126  return *m_options;
127 }
128 
130 {
132  return *m_progress;
133 }
134 
135 void ISimulation::subscribe(const std::function<bool(size_t)>& inform)
136 {
138  m_progress->subscribe(inform);
139 }
140 
141 //! Initializes a progress monitor that prints to stdout.
143 {
144  m_progress->subscribe([](size_t percentage_done) -> bool {
145  if (percentage_done < 100)
146  std::cout << std::setprecision(2) << "\r... " << percentage_done << "%" << std::flush;
147  else // wipe out
148  std::cout << "\r... 100%\n";
149  return true;
150  });
151 }
152 
153 //! Runs simulation with possible averaging over parameter distributions; returns result.
155 {
157  throw std::runtime_error("Sample contains incompatible material definitions");
158  gsl_set_error_handler_off();
159 
161 
162  const auto re_sample = reSample::make(*sample(), options(), force_polarized());
163 
164  const size_t total_size = numberOfElements();
165  size_t param_combinations = m_distribution_handler.getTotalNumberOfSamples();
166 
167  m_progress->reset();
168  m_progress->setExpectedNTicks(param_combinations * total_size);
169 
170  // restrict calculation to current batch
171  const size_t n_batches = m_options->getNumberOfBatches();
172  const size_t current_batch = m_options->getCurrentBatch();
173 
174  const size_t batch_start = startIndex(n_batches, current_batch, total_size);
175  const size_t batch_size = batchSize(n_batches, current_batch, total_size);
176  ASSERT(batch_size);
177 
178  if (param_combinations == 1)
179  runSingleSimulation(re_sample, batch_start, batch_size, 1.);
180  else {
182  for (size_t index = 0; index < param_combinations; ++index) {
183  double weight = m_distribution_handler.setParameterValues(index);
184  runSingleSimulation(re_sample, batch_start, batch_size, weight);
185  }
186  }
188  return pack_result();
189 }
190 
192 {
193  return m_sample.get();
194 }
195 
197 {
198  m_background.reset(bg.clone());
199 }
200 
201 std::vector<const INode*> ISimulation::nodeChildren() const
202 {
203  std::vector<const INode*> result;
204  if (m_sample)
205  result << m_sample.get();
206  return result;
207 }
208 
210 {
211  switch (which) {
213  return "nm";
216  return "rad";
217  default:
218  throw std::runtime_error("ISimulation::unitOfParameter(): missing implementation");
219  }
220 }
221 
223  const IDistribution1D& distribution, size_t nbr_samples,
224  double sigma_factor, const RealLimits& limits)
225 {
226  ParameterDistribution par_distr(whichParameter, distribution, nbr_samples, sigma_factor,
227  limits);
228  addParameterDistribution(par_distr);
229 }
230 
232 {
233  validateParametrization(par_distr);
235 }
236 
237 const std::vector<ParameterDistribution>& ISimulation::getDistributions() const
238 {
240 }
241 
242 
243 //! Runs a single simulation with fixed parameter values.
244 //! If desired, the simulation is run in several threads.
245 void ISimulation::runSingleSimulation(const reSample& re_sample, size_t batch_start,
246  size_t batch_size, double weight)
247 {
249 
250  const size_t n_threads = m_options->getNumberOfThreads();
251  ASSERT(n_threads > 0);
252 
253  std::vector<std::unique_ptr<IComputation>> computations;
254 
255  for (size_t i_thread = 0; i_thread < n_threads; ++i_thread) {
256  const size_t thread_start = batch_start + startIndex(n_threads, i_thread, batch_size);
257  const size_t thread_size = batchSize(n_threads, i_thread, batch_size);
258  if (thread_size == 0)
259  break;
260  computations.emplace_back(createComputation(re_sample, thread_start, thread_size));
261  }
262  runComputations(computations);
263 
264  normalize(batch_start, batch_size);
265  addBackgroundIntensity(batch_start, batch_size);
266  addDataToCache(weight);
267 }
Defines the macro ASSERT.
#define ASSERT(condition)
Definition: Assert.h:45
Defines a few helper functions.
Defines interface IBackground.
Defines interface IComputation.
Defines interface ISimulation.
Defines class MultiLayer.
Defines namespace SampleUtils::Multilayer.
Defines class ProgressHandler.
Defines class reSample.
Defines class SimulationOptions.
Defines class SimulationResult.
void addParameterDistribution(const std::string &param_name, const IDistribution1D &distribution, size_t nbr_samples, double sigma_factor=0.0, const RealLimits &limits=RealLimits())
add a sampled parameter distribution
double setParameterValues(size_t index)
set the parameter values of the simulation object to a specific combination of values,...
const std::vector< ParameterDistribution > & getDistributions() const
size_t getTotalNumberOfSamples() const
get the total number of parameter value combinations (product of the individual sizes of each paramet...
Abstract base class for background noise, to be added to simulated scattering.
Definition: IBackground.h:27
IBackground * clone() const override=0
Interface for one-dimensional distributions.
Definition: Distributions.h:33
void addParameterDistribution(ParameterDistribution::WhichParameter whichParameter, const IDistribution1D &distribution, size_t nbr_samples, double sigma_factor=0.0, const RealLimits &limits=RealLimits())
std::shared_ptr< MultiLayer > m_sample
Definition: ISimulation.h:133
virtual size_t numberOfElements() const =0
Gets the number of elements this simulation needs to calculate.
virtual void prepareSimulation()=0
Put into a clean state for running a simulation.
virtual void initElementVector()=0
Initializes the vector of ISimulation elements.
ProgressHandler & progress()
virtual void validateParametrization(const ParameterDistribution &) const
Checks the distribution validity for simulation.
Definition: ISimulation.h:120
virtual void addDataToCache(double weight)=0
virtual SimulationResult pack_result()=0
Sets m_result.
void subscribe(const std::function< bool(size_t)> &inform)
const MultiLayer * sample() const
std::vector< const INode * > nodeChildren() const override
Returns all children.
~ISimulation() override
const SimulationOptions & options() const
virtual void moveDataFromCache()=0
virtual void addBackgroundIntensity(size_t start_ind, size_t n_elements)=0
void runSingleSimulation(const reSample &re_sample, size_t batch_start, size_t batch_size, double weight=1.0)
Runs a single simulation with fixed parameter values. If desired, the simulation is run in several th...
const std::vector< ParameterDistribution > & getDistributions() const
virtual std::unique_ptr< IComputation > createComputation(const reSample &re_sample, size_t start, size_t n_elements)=0
Generate a single threaded computation for a given range of simulation elements.
std::shared_ptr< SimulationOptions > m_options
Definition: ISimulation.h:135
std::shared_ptr< ProgressHandler > m_progress
Definition: ISimulation.h:136
virtual bool force_polarized() const =0
Force polarized computation even in absence of sample magnetization or external fields.
ISimulation(const MultiLayer &sample)
SimulationResult simulate()
Run a simulation, and return the result.
std::shared_ptr< IBackground > m_background
Definition: ISimulation.h:137
void setBackground(const IBackground &bg)
virtual void initDistributionHandler()
Definition: ISimulation.h:106
std::string unitOfParameter(ParameterDistribution::WhichParameter which) const
DistributionHandler m_distribution_handler
Definition: ISimulation.h:104
virtual void normalize(size_t start_ind, size_t n_elements)=0
Normalize the detector counts to beam intensity, to solid angle, and to exposure angle.
void setTerminalProgressMonitor()
Initializes a progress monitor that prints to stdout.
Our sample model: a stack of layers one below the other.
Definition: MultiLayer.h:43
A parametric distribution function, for use with any model parameter.
Maintains information about progress of a computation.
Limits for a real fit parameter.
Definition: RealLimits.h:24
Collect the different options for simulation.SimulationOptions.
Wrapper around Datafield that also provides unit conversions.
Data structure that contains all the necessary data for scattering calculations.
Definition: ReSample.h:41
static reSample make(const MultiLayer &sample, const SimulationOptions &options, bool forcePolarized=false)
Factory method that wraps the private constructor.
Definition: ReSample.cpp:309
std::string join(const std::vector< std::string > &joinable, const std::string &joint)
Returns string obtain by joining vector elements.
Definition: StringUtils.cpp:45
bool ContainsCompatibleMaterials(const MultiLayer &sample)
Returns true if the sample contains non-default materials of one type only.