45 std::function<T(
const Material&)> average)
47 const T layer_data = average(layer_mat);
48 T averaged_data = layer_data;
50 averaged_data += admix.fraction * (average(admix.material) - layer_data);
58 const std::string avge_name = layer_mat.
materialName() +
"_avg";
60 const R3 avge_magnetization = averageData<R3>(
64 std::vector<const Material*> materials;
65 materials.push_back(&layer_mat);
67 materials.push_back(&admix.material);
71 throw std::runtime_error(
"Error in createAveragedMaterial(): non-default materials of "
72 "different types used simultaneously.");
76 auto avrData = [](
const Material& mat) -> complex_t {
78 return mdc * mdc - 2.0 * mdc;
80 const complex_t avr_mat_data = std::conj(
81 1.0 - std::sqrt(1.0 + averageData<complex_t>(layer_mat, admixtures, avrData)));
89 const auto avr_mat_data = averageData<complex_t>(layer_mat, admixtures, avrData);
90 return MaterialBySLD(avge_name, avr_mat_data.real(), avr_mat_data.imag(),
96 void checkVolumeFractions(
const Admixtures& admixtures)
98 double total_fraction = 0.0;
99 for (
const auto& admix : admixtures)
100 total_fraction += admix.fraction;
101 ASSERT(total_fraction >= 0);
102 if (total_fraction > 1)
103 throw std::runtime_error(
"average slices: "
104 "total volumetric fraction of particles exceeds 1!");
110 for (
const reLayout& layout : layouts) {
111 for (
const auto& entry : layout.regionMap()) {
112 const size_t i_slice = entry.first;
113 if (i_slice == 0 || i_slice == result.size() - 1)
115 const auto slice_mat = result[i_slice].material();
116 checkVolumeFractions(entry.second);
117 result[i_slice].setMaterial(createAveragedMaterial(slice_mat, entry.second));
120 ASSERT(result.size() == stack.size());
140 throw std::runtime_error(
"Cannot process layer with numberOfSlices=0");
141 const ZLimits& slice_limits = layer_limits[i];
145 if (roughness && roughness->
sigma() <= 0)
148 if (!slice_limits.
isFinite() ||
N == 0) {
154 result.
addSlice(tl, *material, roughness);
157 const double top = slice_limits.
zTop();
158 const double bottom = slice_limits.
zBottom();
162 throw std::runtime_error(
"reSample::reSample: "
163 "top limit for top layer must be > 0.");
172 result.
addSlice(-top, *material, roughness);
175 result.
addNSlices(
N, top - bottom, *material, roughness);
179 result.
addSlice(bottom + tl, *material);
188 size_t zToSliceIndex(
double z,
const SliceStack& slices)
190 auto n_layers = slices.size();
191 if (n_layers < 2 || z > 0.0)
194 double z0 = slices[0].zBottom();
196 while (result < n_layers - 1) {
198 if (slices[result].zBottom() - z0 < z)
204 std::pair<size_t, size_t> SliceIndexSpan(
const IParticle& particle,
const SliceStack& slices,
211 (ztop - zbottom) * 1e-6;
213 const double zmax = ztop + z_ref - eps;
214 const double zmin = zbottom + z_ref + eps;
215 size_t top_index = zToSliceIndex(zmax, slices);
216 const size_t bottom_index = zToSliceIndex(zmin, slices);
217 if (top_index > bottom_index)
218 top_index = bottom_index;
219 return {top_index, bottom_index};
226 ASSERT(layout_abundance > 0);
229 std::vector<std::unique_ptr<const CoherentFFSum>> formfactors;
230 std::map<size_t, Admixtures> slice2admixtures;
232 for (
const auto& particle : layout.
particles()) {
234 std::vector<std::shared_ptr<const SumDWBA>> terms;
236 for (
const auto& subparticle : particle->
decompose()) {
237 const auto slice_indices = SliceIndexSpan(*subparticle, slices, z_ref);
238 bool single_layer = (slice_indices.first == slice_indices.second);
239 double z0 = slices.at(0).zBottom();
240 for (
size_t iSlice = slice_indices.first; iSlice < slice_indices.second + 1; ++iSlice) {
241 const Slice& slice = slices[iSlice];
242 double z_top_i = iSlice == 0 ? 0 : slice.
zTop() - z0;
243 R3 translation(0.0, 0.0, z_ref - z_top_i);
245 subparticle->translate(translation);
250 limits = {slice.
zBottom() - z1, slice.
zTop() - z1};
254 pis.
particleSlice->setAmbientMaterial(slices.at(iSlice).material());
256 std::unique_ptr<SumDWBA> computer;
257 if (slices.size() > 1)
258 computer = std::make_unique<SumDWBA>(*pis.
particleSlice, iSlice);
261 terms.emplace_back(computer.release());
263 double factor = particle->
abundance() / layout_abundance * surface_density;
264 if (
double thickness = slice.
thicknessOr0(); thickness > 0.0)
267 admixture.fraction *= factor;
269 slice2admixtures[iSlice].insert(slice2admixtures[iSlice].begin(),
274 formfactors.emplace_back(
275 std::make_unique<CoherentFFSum>(particle->
abundance() / layout_abundance, terms));
280 return reLayout(polarized, surface_density, std::move(formfactors),
281 iff ? iff->clone() :
nullptr, std::move(slice2admixtures));
289 std::vector<reLayout> result;
291 double z_ref = -slices.front().zBottom();
297 result.emplace_back(makereLayout(*layout, slices, z_ref, polarized));
317 std::vector<reLayout>
layouts = collectLayouts(
sample, slices1, polarized);
328 , m_polarized(polarized)
329 , m_layouts(std::move(layouts))
330 , m_stack(refined_stack)
363 return m_stack.at(i).zBottom();
378 for (
const auto& slice :
m_stack)
401 if (xCorrLength <= 0.0)
407 if (!rough_j || !rough_k)
409 const double sigma_j = rough_j->
sigma();
410 const double sigma_k = rough_k->
sigma();
411 if (sigma_j <= 0 || sigma_k <= 0)
416 * std::exp(-1 * std::abs(z_j - z_k) / xCorrLength);
Defines the macro ASSERT.
#define ASSERT(condition)
Defines class CoherentFFSum.
Defines namespace Compute::SpecularMagnetic.
Defines namespace Compute::SpecularScalar.
std::vector< std::unique_ptr< const IFlux > > Fluxes
Defines and implements class IFlux.
Defines and implements the interface class IInterference.
Defines interface IParticle.
Defines class LayerRoughness.
Defines class MaterialBySLDImpl.
Declares functions in namespace MaterialUtils.
Defines class MultiLayer.
Defines namespace SampleUtils::Multilayer.
Defines struct ParticleInSlice; there are no member functions to implement.
Defines class ParticleLayout.
Defines function Compute::Slicing::particleRegions.
Defines class RefractiveMaterialImpl.
Defines class SimulationOptions.
Defines function Compute::Slicing::sliceFormFactor.
The list of material admixtures to a slice.
Abstract base class for Particle, ParticleComposition, ParticleCoreShell, MesoCrystal....
virtual std::vector< std::unique_ptr< IParticle > > decompose() const
Decompose in constituent IParticle objects.
bool isMagnetic() const
Returns true if there is any magnetic material in this ISampleNode.
A roughness of interface between two layers.
double spectralFunction(R3 kvec) const
Returns power spectral density of the surface roughness.
double sigma() const
Returns rms of roughness.
A layer in a MultiLayer sample.
std::vector< const ParticleLayout * > layouts() const
unsigned int numberOfSlices() const
const Material * material() const override
Returns nullptr, unless overwritten to return a specific material.
A wrapper for underlying material implementation.
R3 magnetization() const
Get the magnetization (in A/m)
std::string materialName() const
Returns the name of material.
complex_t materialData() const
Returns delta + i beta.
Our sample model: a stack of layers one below the other.
const Layer * layer(size_t i_layer) const
Returns layer with given index.
R3 externalField() const
Returns the external field applied to the sample (units: A/m)
RoughnessModel roughnessModel() const
size_t numberOfLayers() const
double crossCorrLength() const
Returns cross correlation length of roughnesses between interfaces.
Decorator class that adds particles to ISampleNode objects.
double totalAbundance() const
const IInterference * interferenceFunction() const
std::vector< const IParticle * > particles() const
double weightedParticleSurfaceDensity() const
Collect the different options for simulation.SimulationOptions.
bool useAvgMaterials() const
SliceStack setBField(const R3 &externalField)
void addTopSlice(double zbottom, const Material &material)
void addNSlices(size_t n, double thickness, const Material &material, const LayerRoughness *roughness=nullptr)
Adds n times the same slice to the stack.
void addSlice(double thickness, const Material &material, const LayerRoughness *roughness=nullptr)
Data structure containing the data of a single slice, for calculating the Fresnel coefficients.
double thicknessOr0() const
const LayerRoughness * topRoughness() const
An interval. Limits are of type double, and may be infinite. Used for the z-coordinate,...
Data structure that contains preprocessed data for a single layout.
Data structure that contains all the necessary data for scattering calculations.
const Slice & avgeSlice(size_t i) const
size_t numberOfSlices() const
double sliceTopZ(size_t i) const
static reSample make(const MultiLayer &sample, const SimulationOptions &options, bool forcePolarized=false)
Factory method that wraps the private constructor.
double crossCorrSpectralFun(R3 kvec, size_t j, size_t k) const
Fourier transform of the correlation function of roughnesses between the interfaces.
bool hasRoughness() const
bool polarizing() const
Contains magnetic material, or nonzero magnetic field.
const MultiLayer & m_sample
std::vector< reLayout > m_layouts
const std::vector< reLayout > & layouts() const
bool containsMagneticMaterial() const
Fluxes fluxesIn(const R3 &k) const
double sliceBottomZ(size_t i) const
Fluxes fluxesOut(const R3 &k) const
reSample(const reSample &)=delete
const MultiLayer & sample() const
const SliceStack & averageSlices() const
Material RefractiveMaterial(const std::string &name, complex_t refractive_index, R3 magnetization)
ParticleInSlice createParticleInSlice(const IParticle *particle, const ZLimits &)
ZLimits zSpan(const IParticle *particle)
std::vector< ZLimits > particleRegions(const MultiLayer &sample, bool use_slicing)
Calculate z-admixtures occupied by particles.
Fluxes fluxes(const SliceStack &slices, const R3 &k, bool forward)
Computes refraction angle reflection/transmission coefficients for given sliced sample and wavevector...
Fluxes fluxes(const SliceStack &slices, const R3 &k)
Computes refraction angles and transmission/reflection coefficients for given coherent wave propagati...
MATERIAL_TYPES checkMaterialTypes(const std::vector< const Material * > &materials)
Checks if all non-default materials in materials are of the same type and returns this type....
const LayerRoughness * LayerTopRoughness(const MultiLayer &sample, size_t i)
Returns top roughness of layer.
An admixture to a slice, consisting of a certain volume fraction of a homogeneous material.
Struct that contains information on a sliced particle. This information is needed for evaluating the ...
std::unique_ptr< IReParticle > particleSlice