Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions PWGDQ/Core/VarManager.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@
int VarManager::fgITSROFlength = 100;
int VarManager::fgITSROFBorderMarginLow = 0;
int VarManager::fgITSROFBorderMarginHigh = 0;
int64_t VarManager::fgBCSOR = -1;
int64_t VarManager::fgNBCsPerTF = -1;
int VarManager::fgTFBorderMarginLow = 300;
int VarManager::fgTFBorderMarginHigh = 4000;
uint64_t VarManager::fgSOR = 0;
uint64_t VarManager::fgEOR = 0;
ROOT::Math::PxPyPzEVector VarManager::fgBeamA(0, 0, 6799.99, 6800); // GeV, beam from A-side 4-momentum vector
Expand Down Expand Up @@ -193,8 +197,8 @@
// TO Do: add more systems

// set the beam 4-momentum vectors
float beamAEnergy = energy / 2.0 * sqrt(NumberOfProtonsA * NumberOfProtonsC / NumberOfProtonsC / NumberOfProtonsA); // GeV

Check failure on line 200 in PWGDQ/Core/VarManager.cxx

View workflow job for this annotation

GitHub Actions / O2 linter

[std-prefix]

Use std:: prefix for names from the std namespace.
float beamCEnergy = energy / 2.0 * sqrt(NumberOfProtonsC * NumberOfProtonsA / NumberOfProtonsA / NumberOfProtonsC); // GeV

Check failure on line 201 in PWGDQ/Core/VarManager.cxx

View workflow job for this annotation

GitHub Actions / O2 linter

[std-prefix]

Use std:: prefix for names from the std namespace.
float beamAMomentum = std::sqrt(beamAEnergy * beamAEnergy - NumberOfNucleonsA * NumberOfNucleonsA * MassProton * MassProton);
float beamCMomentum = std::sqrt(beamCEnergy * beamCEnergy - NumberOfNucleonsC * NumberOfNucleonsC * MassProton * MassProton);
fgBeamA.SetPxPyPzE(0, 0, beamAMomentum, beamAEnergy);
Expand Down Expand Up @@ -293,7 +297,7 @@
double mean = calibMeanHist->GetBinContent(binTPCncls, binPin, binEta);
double sigma = calibSigmaHist->GetBinContent(binTPCncls, binPin, binEta);
return (nSigmaValue - mean) / sigma; // Return the calibrated nSigma value
} else if (fgCalibrationType == 2) {

Check failure on line 300 in PWGDQ/Core/VarManager.cxx

View workflow job for this annotation

GitHub Actions / O2 linter

[magic-number]

Avoid magic numbers in expressions. Assign the value to a clearly named variable or constant.
// get the calibration histograms
CalibObjects calibMean, calibSigma, calibStatus;
switch (species) {
Expand Down Expand Up @@ -440,14 +444,14 @@
// Bimodality coefficient = (skewness^2 + 1) / kurtosis
// return a tuple including the coefficient, mean, RMS, skewness, and kurtosis
size_t n = data.size();
if (n < 3) {

Check failure on line 447 in PWGDQ/Core/VarManager.cxx

View workflow job for this annotation

GitHub Actions / O2 linter

[magic-number]

Avoid magic numbers in expressions. Assign the value to a clearly named variable or constant.
return std::make_tuple(-1.0, -1.0, -1.0, -1.0, -1.0);
}
float mean = std::accumulate(data.begin(), data.end(), 0.0) / n;

float m2 = 0.0, m3 = 0.0, m4 = 0.0;
float diff, diff2;
for (float x : data) {

Check failure on line 454 in PWGDQ/Core/VarManager.cxx

View workflow job for this annotation

GitHub Actions / O2 linter

[const-ref-in-for-loop]

Use constant references for non-modified iterators in range-based for loops.
diff = x - mean;
diff2 = diff * diff;
m2 += diff2;
Expand Down Expand Up @@ -486,7 +490,7 @@
int nBins = static_cast<int>((max - min) / binWidth);
std::vector<int> counts(nBins, 0.0);

for (float x : data) {

Check failure on line 493 in PWGDQ/Core/VarManager.cxx

View workflow job for this annotation

GitHub Actions / O2 linter

[const-ref-in-for-loop]

Use constant references for non-modified iterators in range-based for loops.
if (x < min || x >= max) {
continue; // skip out-of-range values
}
Expand Down Expand Up @@ -718,6 +722,14 @@
fgVariableUnits[kMultDimuonsME] = "";
fgVariableNames[kCentFT0C] = "Centrality FT0C";
fgVariableUnits[kCentFT0C] = "%";
fgVariableNames[kIsNoTFBorder] = "Is not TF border";
fgVariableUnits[kIsNoTFBorder] = "";
fgVariableNames[kIsNoTFBorderRecomputed] = "Is not TF border";
fgVariableUnits[kIsNoTFBorderRecomputed] = "";
fgVariableNames[kIsNoITSROFBorder] = "Is not ITS ROF border";
fgVariableUnits[kIsNoITSROFBorder] = "";
fgVariableNames[kIsNoITSROFBorderRecomputed] = "Is not ITS ROF border";
fgVariableUnits[kIsNoITSROFBorderRecomputed] = "";
fgVariableNames[kMCEventGeneratorId] = "MC Generator ID";
fgVariableNames[kMCEventSubGeneratorId] = "MC SubGenerator ID";
fgVariableNames[kMCVtxX] = "MC Vtx X";
Expand All @@ -744,6 +756,10 @@
fgVariableUnits[kMultMCNParticlesEta05] = "Multiplicity_eta05";
fgVariableUnits[kMultMCNParticlesEta08] = "Multiplicity_eta08";
fgVariableUnits[kMultMCNParticlesEta10] = "Multiplicity_eta10";
fgVariableNames[kMCIsNoTFBorderRecomputed] = "MC Is not TF border";
fgVariableUnits[kMCIsNoTFBorderRecomputed] = "";
fgVariableNames[kMCIsNoITSROFBorderRecomputed] = "MC Is not ITS ROF border";
fgVariableUnits[kMCIsNoITSROFBorderRecomputed] = "";
fgVariableNames[kTwoEvPosZ1] = "vtx-z_{1}";
fgVariableUnits[kTwoEvPosZ1] = "cm";
fgVariableNames[kTwoEvPosZ2] = "vtx-z_{2}";
Expand Down Expand Up @@ -1904,8 +1920,11 @@
fgVarNamesMap["kIsPhysicsSelection"] = kIsPhysicsSelection;
fgVarNamesMap["kIsTVXTriggered"] = kIsTVXTriggered;
fgVarNamesMap["kIsNoTFBorder"] = kIsNoTFBorder;
fgVarNamesMap["kIsNoTFBorderRecomputed"] = kIsNoTFBorderRecomputed;
fgVarNamesMap["kIsNoITSROFBorder"] = kIsNoITSROFBorder;
fgVarNamesMap["kIsNoITSROFBorderRecomputed"] = kIsNoITSROFBorderRecomputed;
fgVarNamesMap["kMCIsNoTFBorderRecomputed"] = kMCIsNoTFBorderRecomputed;
fgVarNamesMap["kMCIsNoITSROFBorderRecomputed"] = kMCIsNoITSROFBorderRecomputed;
fgVarNamesMap["kIsNoSameBunch"] = kIsNoSameBunch;
fgVarNamesMap["kIsGoodZvtxFT0vsPV"] = kIsGoodZvtxFT0vsPV;
fgVarNamesMap["kIsVertexITSTPC"] = kIsVertexITSTPC;
Expand Down
27 changes: 25 additions & 2 deletions PWGDQ/Core/VarManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,8 @@
kCollisionRandom, // random number generated per collision (if required, can be used to perform random selections at the collision level)
kIsPhysicsSelection,
kIsTVXTriggered, // Is trigger TVX
kIsNoTFBorder, // No time frame border
kIsNoTFBorder, // No time frame border (from event selection)
kIsNoTFBorderRecomputed, // No time frame border, computed here
kIsNoITSROFBorder, // No ITS read out frame border (from event selection)
kIsNoITSROFBorderRecomputed, // No ITS read out frame border, computed here
kIsNoSameBunch, // No collisions with same T0 BC
Expand Down Expand Up @@ -342,6 +343,8 @@
kMultMCNParticlesEta10,
kMultMCNParticlesEta08,
kMultMCNParticlesEta05,
kMCIsNoITSROFBorderRecomputed,
kMCIsNoTFBorderRecomputed,
kQ1ZNAX,
kQ1ZNAY,
kQ1ZNCX,
Expand Down Expand Up @@ -1504,6 +1507,14 @@
fgITSROFBorderMarginHigh = marginHigh;
}

static void SetTFBorderselection(int64_t bcSOR, int64_t nBCsPerTF, int marginLow, int marginHigh)
{
fgBCSOR = bcSOR;
fgNBCsPerTF = nBCsPerTF;
fgTFBorderMarginLow = marginLow;
fgTFBorderMarginHigh = marginHigh;
}

static void SetSORandEOR(uint64_t sor, uint64_t eor)
{
fgSOR = sor;
Expand Down Expand Up @@ -1533,6 +1544,10 @@
static int fgITSROFlength; // ITS ROF length (from ALPIDE parameters)
static int fgITSROFBorderMarginLow; // ITS ROF border low margin
static int fgITSROFBorderMarginHigh; // ITS ROF border high margin
static int64_t fgBCSOR; // BC for start of run
static int64_t fgNBCsPerTF; // duration of TF in bcs, should be 128*3564 or 32*3564
static int fgTFBorderMarginLow; // TF border low margin
static int fgTFBorderMarginHigh; // TF border high margin
static uint64_t fgSOR; // Timestamp for start of run
static uint64_t fgEOR; // Timestamp for end of run
static ROOT::Math::PxPyPzEVector fgBeamA; // beam from A-side 4-momentum vector
Expand Down Expand Up @@ -1836,9 +1851,9 @@
}
if constexpr ((fillMap & MuonCov) > 0 || (fillMap & ReducedMuonCov) > 0) {
o2::dataformats::GlobalFwdTrack propmuon = PropagateMuon(muontrack, collision);
double px = propmuon.getP() * sin(M_PI / 2 - atan(mfttrack.tgl())) * cos(mfttrack.phi());

Check failure on line 1854 in PWGDQ/Core/VarManager.h

View workflow job for this annotation

GitHub Actions / O2 linter

[std-prefix]

Use std:: prefix for names from the std namespace.
double py = propmuon.getP() * sin(M_PI / 2 - atan(mfttrack.tgl())) * sin(mfttrack.phi());

Check failure on line 1855 in PWGDQ/Core/VarManager.h

View workflow job for this annotation

GitHub Actions / O2 linter

[std-prefix]

Use std:: prefix for names from the std namespace.
double pz = propmuon.getP() * cos(M_PI / 2 - atan(mfttrack.tgl()));

Check failure on line 1856 in PWGDQ/Core/VarManager.h

View workflow job for this annotation

GitHub Actions / O2 linter

[std-prefix]

Use std:: prefix for names from the std namespace.
double pt = std::sqrt(std::pow(px, 2) + std::pow(py, 2));
auto mftprop = o2::aod::fwdtrackutils::getTrackParCovFwdShift(mfttrack, fgzShiftFwd);
values[kX] = mftprop.getX();
Expand Down Expand Up @@ -2116,7 +2131,7 @@
values[kCentVZERO] = event.centRun2V0M();
values[kCentFT0C] = event.centFT0C();
if (fgUsedVars[kIsNoITSROFBorderRecomputed]) {
uint16_t bcInITSROF = (event.globalBC() + 3564 - fgITSROFbias) % fgITSROFlength;
uint16_t bcInITSROF = (event.globalBC() + o2::constants::lhc::LHCMaxBunches - fgITSROFbias) % fgITSROFlength;
values[kIsNoITSROFBorderRecomputed] = bcInITSROF > fgITSROFBorderMarginLow && bcInITSROF < fgITSROFlength - fgITSROFBorderMarginHigh ? 1.0 : 0.0;
}
if (fgUsedVars[kIsNoITSROFBorder]) {
Expand All @@ -2125,6 +2140,10 @@
if (fgUsedVars[kIsTVXTriggered]) {
values[kIsTVXTriggered] = (event.selection_bit(o2::aod::evsel::kIsTriggerTVX) > 0);
}
if (fgUsedVars[kIsNoTFBorderRecomputed]) {
int64_t bcInTF = (event.globalBC() - fgBCSOR) % fgNBCsPerTF;
values[kIsNoTFBorderRecomputed] = bcInTF > fgTFBorderMarginLow && bcInTF < fgNBCsPerTF - fgTFBorderMarginHigh ? 1.0 : 0.0;
}
if (fgUsedVars[kIsNoTFBorder]) {
values[kIsNoTFBorder] = (event.selection_bit(o2::aod::evsel::kNoTimeFrameBorder) > 0);
}
Expand Down Expand Up @@ -2384,6 +2403,10 @@
values[kMultMCNParticlesEta05] = event.multMCNParticlesEta05();
values[kMultMCNParticlesEta08] = event.multMCNParticlesEta08();
values[kMultMCNParticlesEta10] = event.multMCNParticlesEta10();
uint16_t bcInITSROF = (event.globalBC() + o2::constants::lhc::LHCMaxBunches - fgITSROFbias) % fgITSROFlength;
values[kMCIsNoITSROFBorderRecomputed] = bcInITSROF > fgITSROFBorderMarginLow && bcInITSROF < fgITSROFlength - fgITSROFBorderMarginHigh ? 1.0 : 0.0;
int64_t bcInTF = (event.globalBC() - fgBCSOR) % fgNBCsPerTF;
values[kMCIsNoTFBorderRecomputed] = bcInTF > fgTFBorderMarginLow && bcInTF < fgNBCsPerTF - fgTFBorderMarginHigh ? 1.0 : 0.0;
}

if constexpr ((fillMap & EventFilter) > 0 || (fillMap & RapidityGapFilter) > 0) {
Expand Down Expand Up @@ -4703,7 +4726,7 @@
values[kVertexingLxyErr] = (KFPV.GetCovariance(0) + KFGeoTwoProng.GetCovariance(0)) * dxPair2PV * dxPair2PV + (KFPV.GetCovariance(2) + KFGeoTwoProng.GetCovariance(2)) * dyPair2PV * dyPair2PV + 2 * ((KFPV.GetCovariance(1) + KFGeoTwoProng.GetCovariance(1)) * dxPair2PV * dyPair2PV);
values[kVertexingLzErr] = (KFPV.GetCovariance(5) + KFGeoTwoProng.GetCovariance(5)) * dzPair2PV * dzPair2PV;
values[kVertexingLxyzErr] = (KFPV.GetCovariance(0) + KFGeoTwoProng.GetCovariance(0)) * dxPair2PV * dxPair2PV + (KFPV.GetCovariance(2) + KFGeoTwoProng.GetCovariance(2)) * dyPair2PV * dyPair2PV + (KFPV.GetCovariance(5) + KFGeoTwoProng.GetCovariance(5)) * dzPair2PV * dzPair2PV + 2 * ((KFPV.GetCovariance(1) + KFGeoTwoProng.GetCovariance(1)) * dxPair2PV * dyPair2PV + (KFPV.GetCovariance(3) + KFGeoTwoProng.GetCovariance(3)) * dxPair2PV * dzPair2PV + (KFPV.GetCovariance(4) + KFGeoTwoProng.GetCovariance(4)) * dyPair2PV * dzPair2PV);
if (fabs(values[kVertexingLxy]) < 1.e-8f)

Check failure on line 4729 in PWGDQ/Core/VarManager.h

View workflow job for this annotation

GitHub Actions / O2 linter

[std-prefix]

Use std:: prefix for names from the std namespace.
values[kVertexingLxy] = 1.e-8f;
values[kVertexingLxyErr] = values[kVertexingLxyErr] < 0. ? 1.e8f : std::sqrt(values[kVertexingLxyErr]) / values[kVertexingLxy];
if (fabs(values[kVertexingLz]) < 1.e-8f)
Expand Down
9 changes: 8 additions & 1 deletion PWGDQ/DataModel/ReducedInfoTables.h
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,14 @@ DECLARE_SOA_TABLE_VERSIONED(ReducedMCEvents_001, "AOD", "REDUCEDMCEVENT", 1, //!
mccollision::T, mccollision::Weight, mccollision::ImpactParameter, cent::CentFT0C,
mult::MultMCNParticlesEta05, mult::MultMCNParticlesEta08, mult::MultMCNParticlesEta10);

using ReducedMCEvents = ReducedMCEvents_001;
DECLARE_SOA_TABLE_VERSIONED(ReducedMCEvents_002, "AOD", "REDUCEDMCEVENT", 2, //! Event level MC truth information
o2::soa::Index<>,
bc::GlobalBC,
mccollision::GeneratorsID, reducedevent::MCPosX, reducedevent::MCPosY, reducedevent::MCPosZ,
mccollision::T, mccollision::Weight, mccollision::ImpactParameter, cent::CentFT0C,
mult::MultMCNParticlesEta05, mult::MultMCNParticlesEta08, mult::MultMCNParticlesEta10);

using ReducedMCEvents = ReducedMCEvents_002;

using ReducedEvent = ReducedEvents::iterator;
using StoredReducedEvent = StoredReducedEvents::iterator;
Expand Down
6 changes: 4 additions & 2 deletions PWGDQ/TableProducer/tableMakerMC.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -474,7 +474,8 @@ struct TableMakerMC {
eventInfo(collision.globalIndex());
// make an entry for this MC event only if it was not already added to the table
if (!(fEventLabels.find(mcCollision.globalIndex()) != fEventLabels.end())) {
eventMC(mcCollision.generatorsID(), mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(),
auto mcBc = mcCollision.template bc_as<aod::BCsWithTimestamps>();
eventMC(mcBc.globalBC(), mcCollision.generatorsID(), mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(),
mcCollision.t(), mcCollision.weight(), mcCollision.impactParameter(), 1, 1, 1, 1);
fEventLabels[mcCollision.globalIndex()] = fCounters[1];
fCounters[1]++;
Expand Down Expand Up @@ -1109,7 +1110,8 @@ struct TableMakerMC {
eventInfo(collision.globalIndex());
// make an entry for this MC event only if it was not already added to the table
if (!(fEventLabels.find(mcCollision.globalIndex()) != fEventLabels.end())) {
eventMC(mcCollision.generatorsID(), mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(),
auto mcBc = mcCollision.template bc_as<aod::BCsWithTimestamps>();
eventMC(mcBc.globalBC(), mcCollision.generatorsID(), mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(),
mcCollision.t(), mcCollision.weight(), mcCollision.impactParameter(), 1, 1, 1, 1);
fEventLabels[mcCollision.globalIndex()] = fCounters[1];
fCounters[1]++;
Expand Down
3 changes: 2 additions & 1 deletion PWGDQ/TableProducer/tableMakerMC_withAssoc.cxx
Original file line number Diff line number Diff line change
Expand Up @@ -526,12 +526,13 @@ struct TableMakerMC {

// Loop over MC collisions
for (auto& mcCollision : mcCollisions) {
auto bc = mcCollision.template bc_as<BCsWithTimestamps>();
// Get MC collision information into the VarManager
VarManager::FillEvent<gkEventMcFillMapWithCent>(mcCollision);
// Fill histograms
fHistMan->FillHistClass("Event_MCTruth", VarManager::fgValues);
// Create the skimmed table entry for this collision
eventMC(mcCollision.generatorsID(), mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(),
eventMC(bc.globalBC(), mcCollision.generatorsID(), mcCollision.posX(), mcCollision.posY(), mcCollision.posZ(),
mcCollision.t(), mcCollision.weight(), mcCollision.impactParameter(), mcCollision.bestCollisionCentFT0C(),
mcCollision.multMCNParticlesEta05(), mcCollision.multMCNParticlesEta08(), mcCollision.multMCNParticlesEta10());
}
Expand Down
5 changes: 5 additions & 0 deletions PWGDQ/Tasks/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,11 @@ o2physics_add_dpl_workflow(model-converter-mc-reduced-event
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGDQCore
COMPONENT_NAME Analysis)

o2physics_add_dpl_workflow(model-converter-mc-reduced-event-002
SOURCES ModelConverterReducedMCEvents002.cxx
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore O2Physics::PWGDQCore
COMPONENT_NAME Analysis)

o2physics_add_dpl_workflow(tag-and-probe
SOURCES TagAndProbe.cxx
PUBLIC_LINK_LIBRARIES O2::Framework O2::DetectorsBase O2Physics::AnalysisCore O2Physics::AnalysisCCDB O2Physics::PWGDQCore
Expand Down
69 changes: 69 additions & 0 deletions PWGDQ/Tasks/ModelConverterReducedMCEvents002.cxx
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a task. This is a table producer.

Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
// All rights not expressly granted are reserved.
//
// This software is distributed under the terms of the GNU General Public
// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
//
// In applying this license CERN does not waive the privileges and immunities
// granted to it by virtue of its status as an Intergovernmental Organization
// or submit itself to any jurisdiction.
//
// Contact: iarsene@cern.ch, i.c.arsene@fys.uio.no
//
// Task used to convert the data model from the old format to the new format. To avoid
// the conflict with the old data model.

#include "PWGDQ/DataModel/ReducedInfoTables.h"

#include <Framework/AnalysisDataModel.h>
#include <Framework/AnalysisHelpers.h>
#include <Framework/AnalysisTask.h>
#include <Framework/runDataProcessing.h>

using namespace o2;
using namespace o2::framework;
using namespace o2::framework::expressions;
using namespace o2::aod;

struct reducedMCeventConverter002 {
Produces<aod::ReducedMCEvents_002> reducedMCevent_002;

void init(InitContext const&)
{
if (doprocessV000ToV002 == false && doprocessV001ToV002 == false) {
LOGF(fatal, "Neither processV000ToV002 nor processV001ToV002 is enabled. Please choose one!");
}
if (doprocessV000ToV002 == true && doprocessV001ToV002 == true) {
LOGF(fatal, "Both processV000ToV002 and processV001ToV002 are enabled. Please choose only one!");
}
}

void processV000ToV002(aod::ReducedMCEvents_000 const& events)
{
for (const auto& event : events) {
uint64_t globalBc = 0;
reducedMCevent_002(globalBc, event.generatorsID(), event.mcPosX(), event.mcPosY(), event.mcPosZ(),
event.t(), event.weight(), event.impactParameter(),
-1.0f, -1.0f, -1.0f, -1.0f);
}
}
PROCESS_SWITCH(reducedMCeventConverter002, processV000ToV002, "process v000-to-v002 conversion", false);

void processV001ToV002(aod::ReducedMCEvents_001 const& events)
{
for (const auto& event : events) {
uint64_t globalBc = 0;
reducedMCevent_002(globalBc, event.generatorsID(), event.mcPosX(), event.mcPosY(), event.mcPosZ(),
event.t(), event.weight(), event.impactParameter(),
event.centFT0C(), event.multMCNParticlesEta05(), event.multMCNParticlesEta08(), event.multMCNParticlesEta10());
}
}
PROCESS_SWITCH(reducedMCeventConverter002, processV001ToV002, "process v001-to-v002 conversion", true);
};

WorkflowSpec defineDataProcessing(ConfigContext const& cfgc)
{
return WorkflowSpec{
adaptAnalysisTask<reducedMCeventConverter002>(cfgc)};
}
Loading
Loading