#include "AMultilayer.h"
#include "A2x2ComplexMatrix.h"
#include "AOpticsManager.h"
#include <complex>
#include <iostream>
static const Double_t EPSILON = std::numeric_limits<double>::epsilon();
static const Double_t inf = std::numeric_limits<Double_t>::infinity();
ClassImp(AMultilayer);
AMultilayer::AMultilayer(std::shared_ptr<ARefractiveIndex> top,
std::shared_ptr<ARefractiveIndex> bottom)
: fNthreads(1) {
fRefractiveIndexList.push_back(bottom);
fThicknessList.push_back(inf);
InsertLayer(top, inf);
}
AMultilayer::~AMultilayer() {}
Bool_t AMultilayer::IsForwardAngle(std::complex<Double_t> n,
std::complex<Double_t> theta) const {
if (n.real() * n.imag() < 0) {
Error("IsForwardAngle",
"For materials with gain, it's ambiguous which "
"beam is incoming vs outgoing. See "
"https://arxiv.org/abs/1603.02720 Appendix C.\n"
"n: %.3e + %.3ei angle: %.3e + %.3ei",
n.real(), n.imag(), theta.real(), theta.imag());
}
auto ncostheta = n * std::cos(theta);
Bool_t answer;
if (std::abs(ncostheta.imag()) > 100 * EPSILON) {
answer = ncostheta.imag() > 0;
} else {
answer = ncostheta.real() > 0;
}
std::string error_string(
Form("It's not clear which beam is incoming vs outgoing. Weird"
" index maybe?\n"
"n: %.3e + %.3ei angle: %.3e + %.3ei",
n.real(), n.imag(), theta.real(), theta.imag()));
if (answer == true) {
if (ncostheta.imag() <= -100 * EPSILON)
Error("IsForwardAngle", "%s", error_string.c_str());
if (ncostheta.real() <= -100 * EPSILON)
Error("IsForwardAngle", "%s", error_string.c_str());
if ((n * std::cos(std::conj(theta))).real() <= -100 * EPSILON)
Error("IsForwardAngle", "%s", error_string.c_str());
} else {
if (ncostheta.imag() >= 100 * EPSILON)
Error("IsForwardAngle", "%s", error_string.c_str());
if (ncostheta.real() >= 100 * EPSILON)
Error("IsForwardAngle", "%s", error_string.c_str());
if ((n * std::cos(std::conj(theta))).real() < 100 * EPSILON)
Error("IsForwardAngle", "%s", error_string.c_str());
}
return answer;
}
void AMultilayer::ListSnell(
std::complex<Double_t> th_0,
const std::vector<std::complex<Double_t>>& n_list,
std::vector<std::complex<Double_t>>& th_list) const {
auto num_layers = fRefractiveIndexList.size();
th_list.resize(num_layers);
{
auto n_i = n_list.cbegin();
auto th_i = th_list.begin();
auto n0_sinth0 = (*n_i) * std::sin(th_0);
for (std::size_t i = 0; i < num_layers; ++i) {
*th_i = std::asin(n0_sinth0 / *n_i);
++n_i;
++th_i;
}
}
if (!IsForwardAngle(n_list[0], th_list[0])) {
th_list[0] = TMath::Pi() - th_list[0];
}
if (!IsForwardAngle(n_list.back(), th_list.back())) {
th_list.back() = TMath::Pi() - th_list.back();
}
}
void AMultilayer::InsertLayer(std::shared_ptr<ARefractiveIndex> idx,
Double_t thickness) {
fRefractiveIndexList.insert(fRefractiveIndexList.end() - 1, idx);
fThicknessList.insert(fThicknessList.end() - 1, thickness);
}
void AMultilayer::CoherentTMM(EPolarization polarization,
std::complex<Double_t> th_0, Double_t lam_vac,
Double_t& reflectance,
Double_t& transmittance) const {
auto num_layers = fRefractiveIndexList.size();
std::vector<std::complex<Double_t>> n_list(num_layers);
{
auto n_i = n_list.begin();
auto ref_i = fRefractiveIndexList.cbegin();
for (std::size_t i = 0; i < num_layers; ++i) {
*n_i = (*ref_i)->GetComplexRefractiveIndex(lam_vac);
++n_i;
++ref_i;
}
}
if (std::abs((n_list[0] * std::sin(th_0)).imag()) >= 100 * EPSILON ||
!IsForwardAngle(n_list[0], th_0)) {
Error("CoherentTMM", "Error in n0 or th0!");
}
std::vector<std::complex<Double_t>> th_list;
ListSnell(th_0, n_list, th_list);
std::vector<std::complex<Double_t>> kz_list(num_layers);
std::vector<std::complex<Double_t>> cos_th_list(num_layers);
{
auto kz_i = kz_list.begin();
auto n_i = n_list.cbegin();
auto th_i = th_list.cbegin();
auto cos_th_i = cos_th_list.begin();
for (std::size_t i = 0; i < num_layers; ++i) {
*cos_th_i = std::cos(*th_i);
*kz_i = TMath::TwoPi() * (*n_i) * (*cos_th_i) / lam_vac;
++kz_i;
++n_i;
++th_i;
++cos_th_i;
}
}
std::vector<std::complex<Double_t>> delta(num_layers);
{
auto delta_i = delta.begin();
auto kz_i = kz_list.cbegin();
auto thickness_i = fThicknessList.cbegin();
for (std::size_t i = 0; i < num_layers; ++i) {
*delta_i = (*kz_i) * (*thickness_i);
++delta_i;
++kz_i;
++thickness_i;
}
}
static Bool_t opacity_warning = kFALSE;
{
auto delta_i = delta.begin();
++delta_i;
for (std::size_t i = 1; i < num_layers - 1; ++i) {
if ((*delta_i).imag() > 35) {
*delta_i = (*delta_i).real() + std::complex<Double_t>(0, 35);
if (opacity_warning == kFALSE) {
opacity_warning = kTRUE;
Error("CoherentTMM",
"Warning: Layers that are almost perfectly opaque "
"are modified to be slightly transmissive, "
"allowing 1 photon in 10^30 to pass through. It's "
"for numerical stability. This warning will not "
"be shown again.");
}
}
++delta_i;
}
}
std::vector<std::complex<Double_t>> t_list(num_layers);
std::vector<std::complex<Double_t>> r_list(num_layers);
{
auto t_i = t_list.begin();
auto r_i = r_list.begin();
auto th_i = th_list.cbegin();
auto th_f = th_list.cbegin();
++th_f;
auto n_i = n_list.cbegin();
auto n_f = n_list.cbegin();
++n_f;
auto cos_th_i = cos_th_list.cbegin();
auto cos_th_f = cos_th_list.cbegin();
++cos_th_f;
for (std::size_t i = 0; i < num_layers - 1; ++i) {
auto ii = *n_i * (*cos_th_i);
if (polarization == kS) {
auto ff = *n_f * (*cos_th_f);
*t_i = 2. * ii / (ii + ff);
*r_i = (ii - ff) / (ii + ff);
} else {
auto fi = *n_f * (*cos_th_i);
auto if_ = *n_i * (*cos_th_f);
*t_i = 2. * ii / (fi + if_);
*r_i = (fi - if_) / (fi + if_);
}
++t_i;
++r_i;
++th_i;
++th_f;
++n_i;
++n_f;
++cos_th_i;
++cos_th_f;
}
}
std::vector<A2x2ComplexMatrix> M_list(num_layers);
{
const std::complex<Double_t> j(0, 1);
auto M_i = M_list.begin();
++M_i;
auto t_i = t_list.cbegin();
++t_i;
auto r_i = r_list.cbegin();
++r_i;
auto delta_i = delta.cbegin();
++delta_i;
for (std::size_t i = 1; i < num_layers - 1; ++i) {
auto j_delta_i = j * (*delta_i);
*M_i =
1. / (*t_i) *
A2x2ComplexMatrix(std::exp(-j_delta_i), 0, 0, std::exp(j_delta_i)) *
A2x2ComplexMatrix(1, *r_i, *r_i, 1);
++M_i;
++t_i;
++r_i;
++delta_i;
}
}
A2x2ComplexMatrix Mtilde(1, 0, 0, 1);
{
auto M_i = M_list.cbegin();
++M_i;
for (std::size_t i = 1; i < num_layers - 1; ++i) {
Mtilde = Mtilde * (*M_i);
++M_i;
}
}
Mtilde = A2x2ComplexMatrix(1, r_list[0], r_list[0], 1) / t_list[0] * Mtilde;
auto r = Mtilde.Get10() / Mtilde.Get00();
auto t = 1. / Mtilde.Get00();
reflectance = std::abs(r) * std::abs(r);
auto n_i = n_list[0];
auto n_f = n_list.back();
auto th_i = th_0;
auto th_f = th_list.back();
if (polarization == kS) {
transmittance = std::abs(t * t) * (((n_f * std::cos(th_f)).real()) /
(n_i * std::cos(th_i)).real());
} else {
transmittance =
std::abs(t * t) * (((n_f * std::conj(std::cos(th_f))).real()) /
(n_i * std::conj(std::cos(th_i))).real());
}
}
void AMultilayer::PrintLayers(Double_t lambda) const {
auto n = fRefractiveIndexList.size();
for (std::size_t i = 0; i < n; ++i) {
std::cout << "----------------------------------------\n";
std::cout << i << "\tn_i = "
<< fRefractiveIndexList[i]->GetComplexRefractiveIndex(lambda)
<< "\td_i = " << fThicknessList[i] / AOpticsManager::nm()
<< " (nm)\n";
}
std::cout << "----------------------------------------" << std::endl;
}
void AMultilayer::SetNthreads(std::size_t n) {
if (n == 0) {
fNthreads =
std::thread::hardware_concurrency();
if (fNthreads == 0) fNthreads = 1;
} else if (n > 0) {
fNthreads = n;
}
}