-
Yurov, Dmitry authored
Redmine: #1858
Yurov, Dmitry authoredRedmine: #1858
MainComputation.cpp 6.66 KiB
// ************************************************************************** //
//
// BornAgain: simulate and fit scattering at grazing incidence
//
//! @file Core/Computation/MainComputation.cpp
//! @brief Implements class MainComputation.
//!
//! @homepage http://www.bornagainproject.org
//! @license GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2015
//! @authors Scientific Computing Group at MLZ Garching
//! @authors C. Durniak, M. Ganeva, G. Pospelov, W. Van Herck, J. Wuttke
//
// ************************************************************************** //
#include "MainComputation.h"
#include "ParticleLayoutComputation.h"
#include "Layer.h"
#include "IFresnelMap.h"
#include "Instrument.h"
#include "MatrixFresnelMap.h"
#include "MultiLayer.h"
#include "RoughMultiLayerComputation.h"
#include "SpecularComputation.h"
#include "ScalarFresnelMap.h"
#include "ProgressHandler.h"
#include "SimulationElement.h"
namespace
{
HomogeneousMaterial CalculateAverageMaterial(const HomogeneousMaterial& layer_mat,
const std::vector<HomogeneousRegion>& regions);
}
MainComputation::MainComputation(
const MultiLayer& multilayer,
const Instrument& instrument,
const SimulationOptions& options,
ProgressHandler& progress,
const std::vector<SimulationElement>::iterator& begin_it,
const std::vector<SimulationElement>::iterator& end_it)
: mP_multi_layer(multilayer.cloneSliced(options.useAvgMaterials()))
, m_instrument(instrument)
, m_sim_options(options)
, m_progress(&progress)
, m_begin_it(begin_it)
, m_end_it(end_it)
{
mP_fresnel_map.reset(createFresnelMap());
bool polarized = mP_multi_layer->containsMagneticMaterial();
size_t nLayers = mP_multi_layer->numberOfLayers();
for (size_t i=0; i<nLayers; ++i) {
const Layer* layer = mP_multi_layer->layer(i);
for (auto p_layout : layer->layouts())
m_computation_terms.emplace_back(
new ParticleLayoutComputation(
mP_multi_layer.get(), mP_fresnel_map.get(), p_layout, i,
m_sim_options, polarized));
}
// scattering from rough surfaces in DWBA
if (mP_multi_layer->hasRoughness())
m_computation_terms.emplace_back(new RoughMultiLayerComputation(mP_multi_layer.get(),
mP_fresnel_map.get()));
if (m_sim_options.includeSpecular())
m_computation_terms.emplace_back(new SpecularComputation(mP_multi_layer.get(),
mP_fresnel_map.get()));
initFresnelMap();
}
MainComputation::~MainComputation()
{}
void MainComputation::run()
{
m_status.setRunning();
try {
runProtected();
m_status.setCompleted();
} catch(const std::exception &ex) {
m_status.setErrorMessage(std::string(ex.what()));
m_status.setFailed();
}
}
// The normalization of the calculated scattering intensities is:
// For nanoparticles: rho * (scattering cross-section/scattering particle)
// For roughness: (scattering cross-section of area S)/S
// For specular peak: |R|^2 * sin(alpha_i) / solid_angle
// This allows them to be added and normalized together to the beam afterwards
void MainComputation::runProtected()
{
// add intensity of all IComputationTerms:
for (auto& comp: m_computation_terms) {
if (!m_progress->alive())
return;
comp->eval(m_progress, m_begin_it, m_end_it );
}
}
IFresnelMap* MainComputation::createFresnelMap()
{
std::unique_ptr<IFresnelMap> P_result;
if (!mP_multi_layer->requiresMatrixRTCoefficients())
P_result.reset(new ScalarFresnelMap());
else
P_result.reset(new MatrixFresnelMap());
// Disable caching of R,T coefficients when using Monte Carlo integration
if (P_result && m_sim_options.isIntegrate()) {
P_result->disableCaching();
}
return P_result.release();
}
std::unique_ptr<MultiLayer> MainComputation::getAveragedMultilayer() const
{
std::map<size_t, std::vector<HomogeneousRegion>> region_map;
for (auto& comp: m_computation_terms) {
comp->mergeRegionMap(region_map);
}
std::unique_ptr<MultiLayer> P_result(mP_multi_layer->clone());
auto last_layer_index = P_result->numberOfLayers()-1;
for (auto& entry : region_map)
{
auto i_layer = entry.first;
if (i_layer==0 || i_layer==last_layer_index)
continue; // skip semi-infinite layers
auto layer_mat = P_result->layerMaterial(i_layer);
if (!checkRegions(entry.second))
throw std::runtime_error("MainComputation::getAveragedMultilayer: "
"total volumetric fraction of particles exceeds 1!");
auto new_mat = CalculateAverageMaterial(layer_mat, entry.second);
P_result->setLayerMaterial(i_layer, new_mat);
}
return P_result;
}
std::unique_ptr<MultiLayer> MainComputation::getMultilayerForFresnel() const
{
std::unique_ptr<MultiLayer> P_result = m_sim_options.useAvgMaterials()
? getAveragedMultilayer()
: std::unique_ptr<MultiLayer>(mP_multi_layer->clone());
P_result->initBFields();
return P_result;
}
void MainComputation::initFresnelMap()
{
auto multilayer = getMultilayerForFresnel();
mP_fresnel_map->setMultilayer(*multilayer);
}
bool MainComputation::checkRegions(const std::vector<HomogeneousRegion>& regions) const
{
double total_fraction = 0;
for (auto& region : regions)
total_fraction += region.m_volume;
return (total_fraction >= 0 && total_fraction <= 1);
}
namespace
{
HomogeneousMaterial CalculateAverageMaterial(const HomogeneousMaterial& layer_mat,
const std::vector<HomogeneousRegion>& regions)
{
kvector_t magnetization_layer = layer_mat.magnetization();
complex_t refr_index2_layer = layer_mat.refractiveIndex2();
kvector_t magnetization_avg = magnetization_layer;
complex_t refr_index2_avg = refr_index2_layer;
for (auto& region : regions)
{
kvector_t magnetization_region = region.m_material.magnetization();
complex_t refr_index2_region = region.m_material.refractiveIndex2();
magnetization_avg += region.m_volume*(magnetization_region - magnetization_layer);
refr_index2_avg += region.m_volume*(refr_index2_region - refr_index2_layer);
}
complex_t refr_index_avg = std::sqrt(refr_index2_avg);
HomogeneousMaterial result(layer_mat.getName()+"_avg", refr_index_avg, magnetization_avg);
return result;
}
}