Skip to content

Commit 14de1af

Browse files
authored
Merge branch 'AliceO2Group:master' into master
2 parents 9d5d902 + e5cfa50 commit 14de1af

37 files changed

+3541
-2413
lines changed

Common/TableProducer/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ o2physics_add_dpl_workflow(fwdtrackextension
9898
SOURCES fwdtrackextension.cxx
9999
PUBLIC_LINK_LIBRARIES O2Physics::AnalysisCore
100100
O2::ReconstructionDataFormats
101-
O2::DetectorsBase
102-
O2::DetectorsCommonDataFormats
101+
O2::GlobalTracking
102+
O2::MCHTracking
103103
COMPONENT_NAME Analysis)
104104

105105
o2physics_add_dpl_workflow(track-to-collision-associator

Common/TableProducer/PID/pidTPC.cxx

Lines changed: 19 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -103,17 +103,17 @@ struct tpcPid {
103103
// TPC PID Response
104104
o2::pid::tpc::Response* response;
105105

106+
// CCDB accessor
107+
Service<o2::ccdb::BasicCCDBManager> ccdb;
108+
106109
// Network correction for TPC PID response
107110
OnnxModel network;
108-
o2::ccdb::CcdbApi ccdbApi;
109111
std::map<std::string, std::string> metadata;
110-
std::map<std::string, std::string> nullmetadata;
111112
std::map<std::string, std::string> headers;
112113
std::vector<int> speciesNetworkFlags = std::vector<int>(9);
113114
std::string networkVersion;
114115

115116
// Input parameters
116-
Service<o2::ccdb::BasicCCDBManager> ccdb;
117117
Configurable<std::string> paramfile{"param-file", "", "Path to the parametrization object, if empty the parametrization is not taken from file"};
118118
Configurable<std::string> url{"ccdb-url", "http://alice-ccdb.cern.ch", "url of the ccdb repository"};
119119
Configurable<std::string> ccdbPath{"ccdbPath", "Analysis/PID/TPC/Response", "Path of the TPC parametrization on the CCDB"};
@@ -242,21 +242,18 @@ struct tpcPid {
242242
ccdb->setCaching(true);
243243
ccdb->setLocalObjectValidityChecking();
244244
ccdb->setCreatedNotAfter(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
245-
ccdbApi.init(url);
246245
if (time != 0) {
247246
LOGP(info, "Initialising TPC PID response for fixed timestamp {} and reco pass {}:", time, recoPass.value);
248247
ccdb->setTimestamp(time);
249-
response = ccdb->getSpecific<o2::pid::tpc::Response>(path, time, metadata);
250-
headers = ccdbApi.retrieveHeaders(path, metadata, time);
248+
response = ccdb->getSpecific<o2::pid::tpc::Response>(path, time, metadata, &headers);
251249
if (!response) {
252250
LOGF(warning, "Unable to find TPC parametrisation for specified pass name - falling back to latest object");
253-
response = ccdb->getForTimeStamp<o2::pid::tpc::Response>(path, time);
254-
headers = ccdbApi.retrieveHeaders(path, metadata, time);
255-
networkVersion = headers["NN-Version"];
251+
response = ccdb->getForTimeStamp<o2::pid::tpc::Response>(path, time, &headers);
256252
if (!response) {
257253
LOGF(fatal, "Unable to find any TPC object corresponding to timestamp {}!", time);
258254
}
259255
}
256+
networkVersion = headers["NN-Version"];
260257
LOG(info) << "Successfully retrieved TPC PID object from CCDB for timestamp " << time << ", period " << headers["LPMProductionTag"] << ", recoPass " << headers["RecoPassName"];
261258
metadata["RecoPassName"] = headers["RecoPassName"]; // Force pass number for NN request to match retrieved BB
262259
response->PrintAll();
@@ -274,8 +271,7 @@ struct tpcPid {
274271
if (ccdbTimestamp > 0) {
275272
/// Fetching network for specific timestamp
276273
LOG(info) << "Fetching network for timestamp: " << ccdbTimestamp.value;
277-
bool retrieveSuccess = ccdbApi.retrieveBlob(networkPathCCDB.value, ".", metadata, ccdbTimestamp.value, false, networkPathLocally.value);
278-
headers = ccdbApi.retrieveHeaders(networkPathCCDB.value, metadata, ccdbTimestamp.value);
274+
bool retrieveSuccess = ccdb->getCCDBAccessor().retrieveBlob(networkPathCCDB.value, ".", metadata, ccdbTimestamp.value, false, networkPathLocally.value, "", "", &headers);
279275
networkVersion = headers["NN-Version"];
280276
if (retrieveSuccess) {
281277
network.initModel(networkPathLocally.value, enableNetworkOptimizations.value, networkSetNumThreads.value, strtoul(headers["Valid-From"].c_str(), NULL, 0), strtoul(headers["Valid-Until"].c_str(), NULL, 0));
@@ -318,32 +314,29 @@ struct tpcPid {
318314
} else {
319315
LOGP(info, "Retrieving TPC Response for timestamp {} and recoPass {}:", bc.timestamp(), recoPass.value);
320316
}
321-
response = ccdb->getSpecific<o2::pid::tpc::Response>(ccdbPath.value, bc.timestamp(), metadata);
322-
headers = ccdbApi.retrieveHeaders(ccdbPath.value, metadata, bc.timestamp());
323-
networkVersion = headers["NN-Version"];
317+
response = ccdb->getSpecific<o2::pid::tpc::Response>(ccdbPath.value, bc.timestamp(), metadata, &headers);
324318
if (!response) {
325319
LOGP(warning, "!! Could not find a valid TPC response object for specific pass name {}! Falling back to latest uploaded object.", metadata["RecoPassName"]);
326-
headers = ccdbApi.retrieveHeaders(ccdbPath.value, nullmetadata, bc.timestamp());
327-
response = ccdb->getForTimeStamp<o2::pid::tpc::Response>(ccdbPath.value, bc.timestamp());
320+
response = ccdb->getForTimeStamp<o2::pid::tpc::Response>(ccdbPath.value, bc.timestamp(), &headers);
328321
if (!response) {
329322
LOGP(fatal, "Could not find ANY TPC response object for the timestamp {}!", bc.timestamp());
330323
}
331324
}
325+
networkVersion = headers["NN-Version"];
332326
LOG(info) << "Successfully retrieved TPC PID object from CCDB for timestamp " << bc.timestamp() << ", period " << headers["LPMProductionTag"] << ", recoPass " << headers["RecoPassName"];
333327
metadata["RecoPassName"] = headers["RecoPassName"]; // Force pass number for NN request to match retrieved BB
334328
response->PrintAll();
335329
}
336330

337331
if (bc.timestamp() < network.getValidityFrom() || bc.timestamp() > network.getValidityUntil()) { // fetches network only if the runnumbers change
338332
LOG(info) << "Fetching network for timestamp: " << bc.timestamp();
339-
bool retrieveSuccess = ccdbApi.retrieveBlob(networkPathCCDB.value, ".", metadata, bc.timestamp(), false, networkPathLocally.value);
340-
headers = ccdbApi.retrieveHeaders(networkPathCCDB.value, metadata, bc.timestamp());
333+
bool retrieveSuccess = ccdb->getCCDBAccessor().retrieveBlob(networkPathCCDB.value, ".", metadata, bc.timestamp(), false, networkPathLocally.value, "", "", &headers);
341334
networkVersion = headers["NN-Version"];
342335
if (retrieveSuccess) {
343336
network.initModel(networkPathLocally.value, enableNetworkOptimizations.value, networkSetNumThreads.value, strtoul(headers["Valid-From"].c_str(), NULL, 0), strtoul(headers["Valid-Until"].c_str(), NULL, 0));
344337
std::vector<float> dummyInput(network.getNumInputNodes(), 1.);
345338
network.evalModel(dummyInput);
346-
LOGP(info, "Retrieved NN corrections for production tag {}, pass number {}, NN-Version number{}", headers["LPMProductionTag"], headers["RecoPassName"], headers["NN-Version"]);
339+
LOGP(info, "Retrieved NN corrections for production tag {}, pass number {}, NN-Version number {}", headers["LPMProductionTag"], headers["RecoPassName"], headers["NN-Version"]);
347340
} else {
348341
LOG(fatal) << "No valid NN object found matching retrieved Bethe-Bloch parametrisation for pass " << metadata["RecoPassName"] << ". Please ensure that the requested pass has dedicated NN corrections available";
349342
}
@@ -553,12 +546,10 @@ struct tpcPid {
553546
} else {
554547
LOGP(info, "Retrieving TPC Response for timestamp {} and recoPass {}:", bc.timestamp(), recoPass.value);
555548
}
556-
response = ccdb->getSpecific<o2::pid::tpc::Response>(ccdbPath.value, bc.timestamp(), metadata);
557-
headers = ccdbApi.retrieveHeaders(ccdbPath.value, metadata, bc.timestamp());
549+
response = ccdb->getSpecific<o2::pid::tpc::Response>(ccdbPath.value, bc.timestamp(), metadata, &headers);
558550
if (!response) {
559551
LOGP(warning, "!! Could not find a valid TPC response object for specific pass name {}! Falling back to latest uploaded object.", metadata["RecoPassName"]);
560-
response = ccdb->getForTimeStamp<o2::pid::tpc::Response>(ccdbPath.value, bc.timestamp());
561-
headers = ccdbApi.retrieveHeaders(ccdbPath.value, nullmetadata, bc.timestamp());
552+
response = ccdb->getForTimeStamp<o2::pid::tpc::Response>(ccdbPath.value, bc.timestamp(), &headers);
562553
if (!response) {
563554
LOGP(fatal, "Could not find ANY TPC response object for the timestamp {}!", bc.timestamp());
564555
}
@@ -651,12 +642,11 @@ struct tpcPid {
651642
} else {
652643
LOGP(info, "Retrieving TPC Response for timestamp {} and recoPass {}:", bc.timestamp(), recoPass.value);
653644
}
654-
response = ccdb->getSpecific<o2::pid::tpc::Response>(ccdbPath.value, bc.timestamp(), metadata);
655-
headers = ccdbApi.retrieveHeaders(ccdbPath.value, metadata, bc.timestamp());
645+
response = ccdb->getSpecific<o2::pid::tpc::Response>(ccdbPath.value, bc.timestamp(), metadata, &headers);
646+
656647
if (!response) {
657648
LOGP(warning, "!! Could not find a valid TPC response object for specific pass name {}! Falling back to latest uploaded object.", metadata["RecoPassName"]);
658-
response = ccdb->getForTimeStamp<o2::pid::tpc::Response>(ccdbPath.value, bc.timestamp());
659-
headers = ccdbApi.retrieveHeaders(ccdbPath.value, nullmetadata, bc.timestamp());
649+
response = ccdb->getForTimeStamp<o2::pid::tpc::Response>(ccdbPath.value, bc.timestamp(), &headers);
660650
if (!response) {
661651
LOGP(fatal, "Could not find ANY TPC response object for the timestamp {}!", bc.timestamp());
662652
}
@@ -737,10 +727,10 @@ struct tpcPid {
737727
} else {
738728
LOGP(info, "Retrieving TPC Response for timestamp {} and recoPass {}:", bc.timestamp(), recoPass.value);
739729
}
740-
response = ccdb->getSpecific<o2::pid::tpc::Response>(ccdbPath.value, bc.timestamp(), metadata);
730+
response = ccdb->getSpecific<o2::pid::tpc::Response>(ccdbPath.value, bc.timestamp(), metadata, &headers);
741731
if (!response) {
742732
LOGP(warning, "!! Could not find a valid TPC response object for specific pass name {}! Falling back to latest uploaded object.", metadata["RecoPassName"]);
743-
response = ccdb->getForTimeStamp<o2::pid::tpc::Response>(ccdbPath.value, bc.timestamp());
733+
response = ccdb->getForTimeStamp<o2::pid::tpc::Response>(ccdbPath.value, bc.timestamp(), &headers);
744734
if (!response) {
745735
LOGP(fatal, "Could not find ANY TPC response object for the timestamp {}!", bc.timestamp());
746736
}

Common/TableProducer/PID/pidTPCService.cxx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ struct pidTpcService {
4747
// CCDB boilerplate declarations
4848
o2::framework::Configurable<std::string> ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"};
4949
Service<o2::ccdb::BasicCCDBManager> ccdb;
50-
o2::ccdb::CcdbApi ccdbApi;
5150

5251
o2::aod::pid::pidTPCProducts products;
5352
o2::aod::pid::pidTPCConfigurables pidTPCopts;
@@ -61,25 +60,24 @@ struct pidTpcService {
6160
ccdb->setCaching(true);
6261
ccdb->setLocalObjectValidityChecking();
6362
ccdb->setCreatedNotAfter(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
64-
ccdbApi.init(ccdburl.value);
6563

6664
// task-specific
67-
pidTPC.init(ccdb, ccdbApi, initContext, pidTPCopts, metadataInfo);
65+
pidTPC.init(ccdb, initContext, pidTPCopts, metadataInfo);
6866
}
6967

7068
void processTracksIU(soa::Join<aod::Collisions, aod::EvSels> const& collisions, soa::Join<aod::TracksIU, aod::TracksCovIU, aod::TracksExtra> const& tracks, aod::BCsWithTimestamps const& bcs)
7169
{
72-
pidTPC.process(ccdb, ccdbApi, bcs, collisions, tracks, static_cast<TObject*>(nullptr), products);
70+
pidTPC.process(ccdb, bcs, collisions, tracks, static_cast<TObject*>(nullptr), products);
7371
}
7472

7573
void processTracksMCIU(soa::Join<aod::Collisions, aod::EvSels> const& collisions, soa::Join<aod::TracksIU, aod::TracksCovIU, aod::TracksExtra, aod::McTrackLabels> const& tracks, aod::BCsWithTimestamps const& bcs, aod::McParticles const&)
7674
{
77-
pidTPC.process(ccdb, ccdbApi, bcs, collisions, tracks, static_cast<TObject*>(nullptr), products);
75+
pidTPC.process(ccdb, bcs, collisions, tracks, static_cast<TObject*>(nullptr), products);
7876
}
7977

8078
void processTracksIUWithTracksQA(soa::Join<aod::Collisions, aod::EvSels> const& collisions, soa::Join<aod::TracksIU, aod::TracksExtra> const& tracks, aod::BCsWithTimestamps const& bcs, aod::TracksQAVersion const& tracksQA)
8179
{
82-
pidTPC.process(ccdb, ccdbApi, bcs, collisions, tracks, tracksQA, products);
80+
pidTPC.process(ccdb, bcs, collisions, tracks, tracksQA, products);
8381
}
8482

8583
PROCESS_SWITCH(pidTpcService, processTracksIU, "Process TracksIU (Run 3)", true);

Common/TableProducer/PID/pidTPCServiceRun2.cxx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ struct pidTpcServiceRun2 {
4747
// CCDB boilerplate declarations
4848
o2::framework::Configurable<std::string> ccdburl{"ccdburl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"};
4949
Service<o2::ccdb::BasicCCDBManager> ccdb;
50-
o2::ccdb::CcdbApi ccdbApi;
5150

5251
o2::aod::pid::pidTPCProducts products;
5352
o2::aod::pid::pidTPCConfigurables pidTPCopts;
@@ -61,20 +60,19 @@ struct pidTpcServiceRun2 {
6160
ccdb->setCaching(true);
6261
ccdb->setLocalObjectValidityChecking();
6362
ccdb->setCreatedNotAfter(std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count());
64-
ccdbApi.init(ccdburl.value);
6563

6664
// task-specific
67-
pidTPC.init(ccdb, ccdbApi, initContext, pidTPCopts, metadataInfo);
65+
pidTPC.init(ccdb, initContext, pidTPCopts, metadataInfo);
6866
}
6967

7068
void processTracks(soa::Join<aod::Collisions, aod::EvSels> const& collisions, soa::Join<aod::Tracks, aod::TracksExtra> const& tracks, aod::BCsWithTimestamps const& bcs)
7169
{
72-
pidTPC.process(ccdb, ccdbApi, bcs, collisions, tracks, static_cast<TObject*>(nullptr), products);
70+
pidTPC.process(ccdb, bcs, collisions, tracks, static_cast<TObject*>(nullptr), products);
7371
}
7472

7573
void processTracksMC(soa::Join<aod::Collisions, aod::EvSels> const& collisions, soa::Join<aod::Tracks, aod::TracksExtra, aod::McTrackLabels> const& tracks, aod::BCsWithTimestamps const& bcs, aod::McParticles const&)
7674
{
77-
pidTPC.process(ccdb, ccdbApi, bcs, collisions, tracks, static_cast<TObject*>(nullptr), products);
75+
pidTPC.process(ccdb, bcs, collisions, tracks, static_cast<TObject*>(nullptr), products);
7876
}
7977

8078
PROCESS_SWITCH(pidTpcServiceRun2, processTracks, "Process Tracks", true);

Common/TableProducer/fwdtrackextension.cxx

Lines changed: 60 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,21 +10,30 @@
1010
// or submit itself to any jurisdiction.
1111

1212
//
13-
// Task performing forward track DCA computation
13+
// \file fwdtrackextension.cxx
14+
// \brief Task performing forward track DCA computation.
15+
// \author Maurice Coquet, maurice.louis.coquet@cern.ch
1416
//
1517

18+
#include "Common/Core/fwdtrackUtilities.h"
1619
#include "Common/DataModel/TrackSelectionTables.h"
1720

21+
#include <CCDB/BasicCCDBManager.h>
22+
#include <DataFormatsParameters/GRPMagField.h>
23+
#include <DetectorsBase/GeometryManager.h>
24+
#include <DetectorsBase/Propagator.h>
1825
#include <Framework/AnalysisDataModel.h>
1926
#include <Framework/AnalysisHelpers.h>
2027
#include <Framework/AnalysisTask.h>
2128
#include <Framework/DataTypes.h>
2229
#include <Framework/runDataProcessing.h>
30+
#include <GlobalTracking/MatchGlobalFwd.h>
2331
#include <ReconstructionDataFormats/TrackFwd.h>
2432

2533
#include <Math/MatrixRepresentationsStatic.h>
2634
#include <Math/SMatrix.h>
2735

36+
#include <string>
2837
#include <vector>
2938

3039
using namespace o2;
@@ -34,30 +43,62 @@ using namespace o2::framework::expressions;
3443
using SMatrix55 = ROOT::Math::SMatrix<double, 5, 5, ROOT::Math::MatRepSym<double, 5>>;
3544
using SMatrix5 = ROOT::Math::SVector<double, 5>;
3645

46+
using MuonsWithCov = soa::Join<aod::FwdTracks, aod::FwdTracksCov>;
47+
3748
struct FwdTrackExtension {
38-
Produces<aod::FwdTracksDCA> extendedTrackQuantities;
49+
Produces<aod::FwdTracksDCA> fwdDCA;
50+
Configurable<std::string> geoPath{"geoPath", "GLO/Config/GeometryAligned", "Path of the geometry file"};
51+
Configurable<std::string> grpmagPath{"grpmagPath", "GLO/Config/GRPMagField", "CCDB path of the GRPMagField object"};
52+
Configurable<std::string> configCcdbUrl{"configCcdbUrl", "http://alice-ccdb.cern.ch", "url of the ccdb repository"};
53+
Configurable<bool> refitGlobalMuon{"refitGlobalMuon", true, "Recompute parameters of global muons"};
54+
55+
Service<o2::ccdb::BasicCCDBManager> fCCDB;
56+
o2::parameters::GRPMagField* grpmag = nullptr; // for run 3, we access GRPMagField from GLO/Config/GRPMagField
57+
int fCurrentRun; // needed to detect if the run changed and trigger update of magnetic field
3958

40-
void process(aod::FwdTracks const& tracks, aod::Collisions const&)
59+
void init(o2::framework::InitContext&)
4160
{
42-
for (auto& track : tracks) {
43-
float dcaX = -999;
44-
float dcaY = -999;
45-
if (track.has_collision()) {
46-
if (track.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack || track.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalForwardTrack || track.trackType() == o2::aod::fwdtrack::ForwardTrackTypeEnum::MuonStandaloneTrack) {
61+
// Load geometry
62+
fCCDB->setURL(configCcdbUrl);
63+
fCCDB->setCaching(true);
64+
fCCDB->setLocalObjectValidityChecking();
4765

48-
auto const& collision = track.collision();
49-
double chi2 = track.chi2();
50-
SMatrix5 tpars(track.x(), track.y(), track.phi(), track.tgl(), track.signed1Pt());
51-
std::vector<double> v1;
52-
SMatrix55 tcovs(v1.begin(), v1.end());
53-
o2::track::TrackParCovFwd pars1{track.z(), tpars, tcovs, chi2};
54-
pars1.propagateToZlinear(collision.posZ());
66+
if (!o2::base::GeometryManager::isGeometryLoaded()) {
67+
LOGF(info, "Load geometry from CCDB");
68+
fCCDB->get<TGeoManager>(geoPath);
69+
}
70+
}
5571

56-
dcaX = (pars1.getX() - collision.posX());
57-
dcaY = (pars1.getY() - collision.posY());
58-
}
72+
void process(aod::Collisions::iterator const& collision, o2::aod::BCsWithTimestamps const& /*...*/, MuonsWithCov const& tracks, aod::MFTTracks const& /*...*/)
73+
{
74+
auto bc = collision.template bc_as<o2::aod::BCsWithTimestamps>();
75+
if (fCurrentRun != bc.runNumber()) {
76+
grpmag = fCCDB->getForTimeStamp<o2::parameters::GRPMagField>(grpmagPath, bc.timestamp());
77+
if (grpmag != nullptr) {
78+
LOGF(info, "Init field from GRP");
79+
o2::base::Propagator::initFieldFromGRP(grpmag);
80+
}
81+
LOGF(info, "Set field for muons");
82+
o2::mch::TrackExtrap::setField();
83+
fCurrentRun = bc.runNumber();
84+
}
85+
const float zField = grpmag->getNominalL3Field();
86+
for (const auto& track : tracks) {
87+
const auto trackType = track.trackType();
88+
o2::dataformats::GlobalFwdTrack fwdtrack = o2::aod::fwdtrackutils::getTrackParCovFwd(track, track);
89+
if (refitGlobalMuon && (trackType == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalMuonTrack || trackType == o2::aod::fwdtrack::ForwardTrackTypeEnum::GlobalForwardTrack)) {
90+
auto muontrack = track.template matchMCHTrack_as<MuonsWithCov>();
91+
auto mfttrack = track.template matchMFTTrack_as<aod::MFTTracks>();
92+
o2::dataformats::GlobalFwdTrack propmuon = o2::aod::fwdtrackutils::propagateMuon(muontrack, muontrack, collision, o2::aod::fwdtrackutils::propagationPoint::kToVertex, 0.f, zField);
93+
SMatrix5 tpars(mfttrack.x(), mfttrack.y(), mfttrack.phi(), mfttrack.tgl(), mfttrack.signed1Pt());
94+
SMatrix55 tcovs{};
95+
o2::track::TrackParCovFwd mft{mfttrack.z(), tpars, tcovs, mfttrack.chi2()};
96+
fwdtrack = o2::aod::fwdtrackutils::refitGlobalMuonCov(propmuon, mft);
5997
}
60-
extendedTrackQuantities(dcaX, dcaY);
98+
const auto proptrack = o2::aod::fwdtrackutils::propagateTrackParCovFwd(fwdtrack, trackType, collision, o2::aod::fwdtrackutils::propagationPoint::kToDCA, 0.f, zField);
99+
const float dcaX = (proptrack.getX() - collision.posX());
100+
const float dcaY = (proptrack.getY() - collision.posY());
101+
fwdDCA(dcaX, dcaY);
61102
}
62103
}
63104
};

0 commit comments

Comments
 (0)