13#include <condition_variable>
58 static constexpr double epsilon = 1.0e-10;
66 static constexpr int number_of_iterations = 10000;
67 static constexpr int number_of_extra_steps = 1;
68 static constexpr double epsilon = 1.0e-4;
72 static const double PMIN = 1.0e-6;
73 static const double TMIN_NS = -2.0;
79 struct experiment_type {
91 friend inline std::istream&
operator>>(std::istream& in, experiment_type& experiment)
93 return in >> experiment.Ha
105 friend inline std::ostream&
operator<<(std::ostream& out,
const experiment_type& experiment)
107 return out << experiment.Ha <<
' '
126 double getChi2(
const double ps)
const
128 const double u = (ratio - ps/value) / error;
160 JEditor(
double& ps,
const range_type& range =
range_type()) :
170 virtual void apply(
const double step)
override
174 this->ps = constrain(this->ps);
203 static inline double getFactorAbsorptionLength()
205 return get_factor_a() * get_factor_b();
214 static inline double getOffsetAbsorptionLength()
216 const double y = 1.0 / get_offset_a() + 1.0 / get_offset_b();
229 const double y = 1.0 / (JAbsorptionLength::getAbsorptionLength(lambda) * getFactorAbsorptionLength()) + 1.0 / getOffsetAbsorptionLength();
243 return JScatteringLength::getScatteringLength(lambda);
252 static inline double getFactorQE()
254 return (get_factor_a() / get_factor_b()) * ((ABSORPTION_LENGTH_M + get_offset_a()) / get_offset_a()) * (get_offset_b() / (ABSORPTION_LENGTH_M + get_offset_b()));
264 static inline double getQE(
const double lambda)
266 return JQE::getQE(lambda) * getFactorQE();
275 static double& get_factor_a()
277 static double a = 1.0;
288 static double& get_factor_b()
290 static double b = 1.0;
301 static double& get_offset_a()
303 static double a = 1.0e6;
314 static double& get_offset_b()
316 static double b = 1.0e6;
321 static constexpr double ABSORPTION_LENGTH_M = 50.0;
334 static const int ND = 2;
343 static bool check(
const double value,
const double error)
345 return (value > 0.0 &&
353 static struct bins_type {
355 static const char SEPARATOR =
'x';
364 friend inline std::istream&
operator>>(std::istream& in, bins_type& bins)
372 const size_t pos = buffer.find(SEPARATOR);
374 if (pos != string::npos) {
376 if (!(istringstream(buffer.substr(0,pos)) >> bins.R_m)) { in.setstate(ios::badbit); }
377 if (!(istringstream(buffer.substr(pos+1)) >> bins.t_ns)) { in.setstate(ios::badbit); }
381 in.setstate(ios::badbit);
395 friend inline std::ostream&
operator<<(std::ostream& out,
const bins_type& bins)
397 return out << bins.R_m << SEPARATOR << bins.t_ns;
416 static double getPs(
const JPDF_C& pdf,
418 const pair_type& R_m,
419 const pair_type& t_ns,
420 const module_type& module)
424 const double dr = (R_m .second - R_m .first) / JWifi::bins.R_m;
425 const double dt = (t_ns.second - t_ns.first) / JWifi::bins.t_ns;
429 for (
const pmt_type& pmt : module) {
431 for (
double r1 = R_m.first + 0.5*dr; r1 < R_m.second; r1 += dr) {
435 for (
double t1 = t_ns.first + 0.5*dt; t1 < t_ns.second; t1 += dt) {
436 npe += pdf.getLightFromMuon(E_GeV, r1, pmt.getTheta(), pmt.getPhi(), t1) * dt;
439 ps += 1.0 - exp(-npe);
443 return ps / (
module.size() * JWifi::bins.R_m);
456 JWifi(
const double E_GeV,
459 const double angle_Deg,
481 const JOmega3D omega(angle_Deg * PI / 180.0);
483 for (JOmega3D::const_iterator i = omega.begin(); i != omega.end(); ++i) {
485 if (i->getTheta() <= 0.5*PI && i->getPhi() <= 0.5*PI)
486 module[0].push_back(*i);
487 else if (i->getTheta() >= 0.5*PI && i->getPhi() >= 0.5*PI)
488 module[1].push_back(*i);
493 const TH2* h2[ND] = { ha, hb };
495 for (
int i = 0; i != ND; ++i) {
497 for (Int_t ix = 1; ix <= h2[i]->GetXaxis()->GetNbins(); ++ix) {
498 for (Int_t iy = 1; iy <= h2[i]->GetYaxis()->GetNbins(); ++iy) {
500 pair_type R_m = { h2[i]->GetXaxis()->GetBinLowEdge(ix), h2[i]->GetXaxis()->GetBinUpEdge(ix) };
501 pair_type t_ns = { h2[i]->GetYaxis()->GetBinLowEdge(iy), h2[i]->GetYaxis()->GetBinUpEdge(iy) };
503 if (t_ns.first < TMIN_NS) {
504 t_ns.first = TMIN_NS;
507 double value = h2[i]->GetBinContent(ix,iy);
508 double error = h2[i]->GetBinError (ix,iy);
510 if (check(value, error)) {
512 const double ps = getPs(pdf, E_GeV, R_m, t_ns, module[i]);
515 buffer[i].push_back({R_m, t_ns, value, error, ps});
531 return buffer[0].size() + buffer[1].size();
541 double operator()(
const int option)
const
547 for (
int i = 0; i != ND; ++i) {
551 for (
const auto& bin : buffer[i]) {
553 const double ps = getPs(pdf, E_GeV, bin.R_m, bin.t_ns, module[i]);
555 chi2 += bin.getChi2(ps);
561 JPerth perth(pdf, E_GeV, buffer[i], module[i], ns);
564 chi2 += JPerth::chi2;
578 module_type module[ND];
597 const module_type& module,
607 for (
size_t i = 0; i < ns; ++i) {
609 thread worker([
this, pdf, E_GeV, module]() {
616 unique_lock<mutex> lock(in);
618 cv.wait(lock, [
this]() {
return stop || this->input.hasNext(); });
620 if (stop && !this->input.hasNext()) {
624 bin = *(this->input.next());
627 const double ps = getPs(pdf, E_GeV, bin.R_m, bin.t_ns, module);
630 unique_lock<mutex> lock(out);
632 chi2 += bin.getChi2(ps);
637 workers.emplace_back(std::move(worker));
650 unique_lock<mutex> lock(in);
657 for (
auto& worker : workers) {
670 std::condition_variable cv;
678 JWifi::bins_type JWifi::bins = { 1, 1};
683 double JWifi::JPerth::chi2 = 0.0;
689 const char*
const absorption_factor_t =
"absorption_factor";
690 const char*
const absorption_offset_t =
"absorption_offset";
691 const char*
const scattering_factor_t =
"scattering_factor";
692 const char*
const qe_factor_t =
"qe_factor";
693 const char*
const mixed_factor_a_t =
"mixed_factor_a";
694 const char*
const mixed_factor_b_t =
"mixed_factor_b";
695 const char*
const mixed_offset_a_t =
"mixed_offset_a";
696 const char*
const mixed_offset_b_t =
"mixed_offset_b";
713 experiment_type setup;
734 <<
"possible options absorptionLength: " << get_keys(absorptionLength) << endl
735 <<
"possible options scatteringLength: " << get_keys(scatteringLength) << endl) =
JPARSER::initialised();
737 "douplet of histograms corresponding to scattered and direct light, "
738 <<
"each of which defined by <file name>:<histogram name>");
739 zap[
'E'] =
make_field(E_GeV,
"muon energy [GeV]") = 10.0;
740 zap[
'G'] =
make_field(angle_Deg,
"grid angle for PMT directions [deg]") = 25.0;
742 zap[
'N'] =
make_field(threads,
"number threads") = 0;
743 zap[
'O'] =
make_field(option,
"fit options: \"parameter <start value> <step size>\"" << endl
744 <<
"possible parameters: "
745 << absorption_factor_t <<
", "
746 << absorption_offset_t <<
", "
747 << scattering_factor_t <<
", "
748 << qe_factor_t <<
", "
749 << mixed_factor_a_t <<
", "
750 << mixed_factor_b_t <<
", "
751 << mixed_offset_a_t <<
", "
752 << mixed_offset_b_t);
753 zap[
'n'] =
make_field(normalise,
"normalise gradient");
759 catch(
const exception& error) {
760 FATAL(error.what() << endl);
764 const TH2* ha =
dynamic_cast<const TH2*
>(getObject(setup.Ha));
765 const TH2* hb =
dynamic_cast<const TH2*
>(getObject(setup.Hb));
767 if (ha == NULL) {
FATAL(
"Missing histogram: " << setup.Ha << endl); }
768 if (hb == NULL) {
FATAL(
"Missing histogram: " << setup.Hb << endl); }
770 const JWifi wifi(E_GeV, ha, hb, angle_Deg, threads);
774 JGradient fit(FIT::number_of_iterations, FIT::number_of_extra_steps, FIT::epsilon, 3);
778 auto fp = [&option, &fit](
const string& key,
double& value,
const range_type& range) {
779 if (option.count(key)) {
780 fit.push_back(
JModifier_t(key,
new JEditor(value = option[key].first, range), option[key].second));
784 fp(absorption_factor_t, JAbsorptionLength::get_factor(),
range_type(0.1, 1.0e1));
785 fp(absorption_offset_t, JAbsorptionLength::get_offset(),
range_type(1.0, 1.0e6));
786 fp(scattering_factor_t, JScatteringLength::get_factor(),
range_type(0.1, 1.0e1));
787 fp(qe_factor_t, JQE ::get_factor(),
range_type(0.1, 1.0e1));
788 fp(mixed_factor_a_t, JMixed ::get_factor_a(),
range_type(0.1, 1.0e1));
789 fp(mixed_factor_b_t, JMixed ::get_factor_b(),
range_type(0.1, 1.0e1));
790 fp(mixed_offset_a_t, JMixed ::get_offset_a(),
range_type(1.0, 1.0e6));
791 fp(mixed_offset_b_t, JMixed ::get_offset_b(),
range_type(1.0, 1.0e6));
793 const chrono::high_resolution_clock::time_point t0 = chrono::high_resolution_clock::now();
795 const double chi2 = fit(wifi);
797 const chrono::high_resolution_clock::time_point t1 = chrono::high_resolution_clock::now();
799 cout <<
"chi2/NDF " <<
FIXED(9,5) << chi2 <<
"/" << setw(4) << (wifi.getN() - fit.size()) << endl;
801 cout <<
"Elapsed time: " << setw(6) << (t1 - t0) / chrono::seconds(1) <<
" [s]" << endl;
803 for (
const auto& i : fit) {
804 cout << left << setw(36) << i.name <<
' ' << right << FIXED(13,3) << dynamic_cast<const JEditor*>(i.get())->getValue() << endl;
807 auto get_offset = [](
const double x1,
const double x2) {
const double y = 1.0/x1 + 1.0/x2;
return 1.0/y; };
808 auto get_factor = [](
const double x1,
const double x2) {
return x1 * x2; };
810 cout <<
"Additional absorption length: " <<
FIXED(13,5) << get_offset(JAbsorptionLength::get_offset(), JMixed::getOffsetAbsorptionLength()) <<
" [m]" << endl;
811 cout <<
"Scaling of absorption length: " <<
FIXED(13,5) << get_factor(JAbsorptionLength::get_factor(), JMixed::getFactorAbsorptionLength()) << endl;
812 cout <<
"Scaling of scattering length: " <<
FIXED(13,5) << JScatteringLength::get_factor() << endl;
813 cout <<
"Scaling of QE: " <<
FIXED(13,5) << get_factor(JQE ::get_factor(), JMixed::getFactorQE()) << endl;
double getAngularAcceptance(const double x)
Angular acceptence of PMT.
std::istream & operator>>(std::istream &in, JAANET::JHead &header)
Read header from input.
General purpose messaging.
Utility class to parse command line options.
#define make_field(A,...)
macro to convert parameter to JParserTemplateElement object
Utility class to parse parameter values.
#define gmake_property(A)
macros to convert (template) parameter to JPropertiesElement object
Auxiliary class to define a range between two values.
Properties of KM3NeT PMT and deep-sea water.
Utility class to parse parameter values.
Data structure for angles in three dimensions.
Direction set covering (part of) solid angle.
Auxiliary class to handle file name, ROOT directory and object name.
Utility class to parse command line options.
Probability Density Functions of the time response of a PMT with an implementation of the JAbstractPM...
double getAmbientPressure()
Get ambient pressure.
double getScatteringLength(const double lambda)
Get scattering length.
double getScatteringProbability(const double x)
Function to describe light scattering in water.
double getPhotocathodeArea()
Get photo-cathode area of PMT.
double getAbsorptionLength(const double lambda)
Get absorption length.
JCombinatorics::pair_type pair_type
double getQE(const double R, const double mu)
Get QE for given ratio of hit probabilities and expectation value of the number of photo-electrons.
double getValue(const JScale_t scale)
Get numerical value corresponding to scale.
double getChi2(const double P)
Get chi2 corresponding to given probability.
std::istream & getline(std::istream &in, JString &object)
Read string from input stream until end of line.
Auxiliary methods for light properties of deep-sea water.
double getMinimalWavelength()
Get minimal wavelength for PDF evaluations.
double getMaximalWavelength()
Get maximal wavelength for PDF evaluations.
This name space includes all other name spaces (except KM3NETDAQ, KM3NET and ANTARES).
JLANG::JSTDObjectReader< const event_type > input_type
std::vector< event_type > data_type
Auxiliary data structure for floating point format specification.
Data structure for measured coincidence rates of all pairs of PMTs in optical module.
bool normalise
normalise gradient
Auxiliary data structure for editable parameter.
Auxiliary data structure for fit parameter.
Implementation of object iteration from STD container.
Empty structure for specification of parser element that is initialised (i.e. does not require input)...
Auxiliary data structure to customize absorption length.
Auxiliary data structure to customize quantum efficiency.
Auxiliary data structure to customize scattering length.
Thread pool for fits to data.