ParallelTTracker.cpp 53.5 KB
Newer Older
gsell's avatar
gsell committed
1
//
2 3
// Class ParallelTTracker
//   OPAL-T tracker.
gsell's avatar
gsell committed
4 5 6
//   The visitor class for tracking particles with time as independent
//   variable.
//
kraus's avatar
kraus committed
7 8 9
// Copyright (c) 200x - 2014, Christof Kraus, Paul Scherrer Institut, Villigen PSI, Switzerland
//               2015 - 2016, Christof Metzger-Kraus, Helmholtz-Zentrum Berlin, Germany
//               2017 - 2020, Christof Metzger-Kraus
10
// All rights reserved
gsell's avatar
gsell committed
11
//
12 13 14 15 16 17 18 19 20
// This file is part of OPAL.
//
// OPAL is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// You should have received a copy of the GNU General Public License
// along with OPAL. If not, see <https://www.gnu.org/licenses/>.
gsell's avatar
gsell committed
21
//
kraus's avatar
kraus committed
22 23
#include "Algorithms/ParallelTTracker.h"

gsell's avatar
gsell committed
24
#include <cfloat>
25
#include <cmath>
gsell's avatar
gsell committed
26
#include <fstream>
27
#include <limits>
gsell's avatar
gsell committed
28
#include <iomanip>
29
#include <iostream>
gsell's avatar
gsell committed
30 31 32
#include <sstream>
#include <string>

33 34
#include "Algorithms/OrbitThreader.h"
#include "Algorithms/CavityAutophaser.h"
35
#include "Beamlines/Beamline.h"
36
#include "Beamlines/FlaggedBeamline.h"
37

38
#include "Solvers/CSRWakeFunction.hh"
gsell's avatar
gsell committed
39 40 41 42

#include "AbstractObjects/OpalData.h"

#include "BasicActions/Option.h"
43
#include "Utilities/Options.h"
44
#include "Utilities/Util.h"
gsell's avatar
gsell committed
45 46

#include "Distribution/Distribution.h"
47
#include "ValueDefinitions/RealVariable.h"
gsell's avatar
gsell committed
48 49
#include "Utilities/Timer.h"
#include "Utilities/OpalException.h"
50
#include "Solvers/ParticleMatterInteractionHandler.hh"
gsell's avatar
gsell committed
51
#include "Structure/BoundaryGeometry.h"
52
#include "AbsBeamline/Monitor.h"
53

54 55 56 57
#ifdef ENABLE_OPAL_FEL
#include "BeamlineCore/UndulatorRep.h"
#endif

gsell's avatar
gsell committed
58 59 60 61 62
class PartData;

ParallelTTracker::ParallelTTracker(const Beamline &beamline,
                                   const PartData &reference,
                                   bool revBeam,
63 64 65
                                   bool revTrack):
    Tracker(beamline, reference, revBeam, revTrack),
    itsDataSink_m(NULL),
66
    itsOpalBeamline_m(beamline.getOrigin3D(), beamline.getInitialDirection()),
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
    globalEOL_m(false),
    wakeStatus_m(false),
    wakeFunction_m(NULL),
    pathLength_m(0.0),
    zstart_m(0.0),
    dtCurrentTrack_m(0.0),
    minStepforReBin_m(-1),
    minBinEmitted_m(std::numeric_limits<size_t>::max()),
    repartFreq_m(-1),
    emissionSteps_m(std::numeric_limits<unsigned int>::max()),
    numParticlesInSimulation_m(0),
    timeIntegrationTimer1_m(IpplTimings::getTimer("TIntegration1")),
    timeIntegrationTimer2_m(IpplTimings::getTimer("TIntegration2")),
    fieldEvaluationTimer_m(IpplTimings::getTimer("External field eval")),
    BinRepartTimer_m(IpplTimings::getTimer("Binaryrepart")),
82
    WakeFieldTimer_m(IpplTimings::getTimer("WakeField")),
83
    particleMatterStatus_m(false)
snuverink_j's avatar
snuverink_j committed
84
{}
gsell's avatar
gsell committed
85 86

ParallelTTracker::ParallelTTracker(const Beamline &beamline,
frey_m's avatar
frey_m committed
87
                                   PartBunchBase<double, 3> *bunch,
gsell's avatar
gsell committed
88 89 90 91
                                   DataSink &ds,
                                   const PartData &reference,
                                   bool revBeam,
                                   bool revTrack,
92
                                   const std::vector<unsigned long long> &maxSteps,
93
                                   double zstart,
94
                                   const std::vector<double> &zstop,
95
                                   const std::vector<double> &dt):
frey_m's avatar
frey_m committed
96
    Tracker(beamline, bunch, reference, revBeam, revTrack),
97
    itsDataSink_m(&ds),
98
    itsOpalBeamline_m(beamline.getOrigin3D(), beamline.getInitialDirection()),
99 100 101 102 103 104 105 106 107
    globalEOL_m(false),
    wakeStatus_m(false),
    wakeFunction_m(NULL),
    pathLength_m(0.0),
    zstart_m(zstart),
    dtCurrentTrack_m(0.0),
    minStepforReBin_m(-1),
    minBinEmitted_m(std::numeric_limits<size_t>::max()),
    repartFreq_m(-1),
108
    emissionSteps_m(std::numeric_limits<unsigned int>::max()),
109 110 111 112 113
    numParticlesInSimulation_m(0),
    timeIntegrationTimer1_m(IpplTimings::getTimer("TIntegration1")),
    timeIntegrationTimer2_m(IpplTimings::getTimer("TIntegration2")),
    fieldEvaluationTimer_m(IpplTimings::getTimer("External field eval")),
    BinRepartTimer_m(IpplTimings::getTimer("Binaryrepart")),
114
    WakeFieldTimer_m(IpplTimings::getTimer("WakeField")),
115
    particleMatterStatus_m(false)
116
{
117 118
    for (unsigned int i = 0; i < zstop.size(); ++ i) {
        stepSizes_m.push_back(dt[i], zstop[i], maxSteps[i]);
119
    }
120

121 122
    stepSizes_m.sortAscendingZStop();
    stepSizes_m.resetIterator();
gsell's avatar
gsell committed
123 124
}

snuverink_j's avatar
snuverink_j committed
125
ParallelTTracker::~ParallelTTracker() {}
gsell's avatar
gsell committed
126

127 128 129
void ParallelTTracker::visitBeamline(const Beamline &bl) {
    const FlaggedBeamline* fbl = static_cast<const FlaggedBeamline*>(&bl);
    if (fbl->getRelativeFlag()) {
130
        OpalBeamline stash(fbl->getOrigin3D(), fbl->getInitialDirection());
131 132 133 134 135 136 137 138 139
        stash.swap(itsOpalBeamline_m);
        fbl->iterate(*this, false);
        itsOpalBeamline_m.prepareSections();
        itsOpalBeamline_m.compute3DLattice();
        stash.merge(itsOpalBeamline_m);
        stash.swap(itsOpalBeamline_m);
    } else {
        fbl->iterate(*this, false);
    }
140
}
141

142
void ParallelTTracker::updateRFElement(std::string elName, double maxPhase) {
143 144
    FieldList cavities = itsOpalBeamline_m.getElementByType(ElementBase::RFCAVITY);
    FieldList travelingwaves = itsOpalBeamline_m.getElementByType(ElementBase::TRAVELINGWAVE);
Christof Metzger-Kraus's avatar
Christof Metzger-Kraus committed
145
    cavities.insert(cavities.end(), travelingwaves.begin(), travelingwaves.end());
146 147

    for (FieldList::iterator fit = cavities.begin(); fit != cavities.end(); ++ fit) {
148 149
        if ((*fit).getElement()->getName() == elName) {

150
            RFCavity *element = static_cast<RFCavity *>((*fit).getElement().get());
151

Christof Metzger-Kraus's avatar
Christof Metzger-Kraus committed
152
            element->setPhasem(maxPhase);
153
            element->setAutophaseVeto();
154

155 156 157
            INFOMSG("Restored cavity phase from the h5 file. Name: " << element->getName() << ", phase: " << maxPhase << " rad" << endl);
            return;
        }
gsell's avatar
gsell committed
158 159 160
    }
}

161 162
void ParallelTTracker::saveCavityPhases() {
    itsDataSink_m->storeCavityInformation();
gsell's avatar
gsell committed
163 164
}

165 166
void ParallelTTracker::restoreCavityPhases() {
    typedef std::vector<MaxPhasesT>::iterator iterator_t;
167

168 169 170 171 172 173
    if (OpalData::getInstance()->hasPriorTrack() ||
        OpalData::getInstance()->inRestartRun()) {
        iterator_t it = OpalData::getInstance()->getFirstMaxPhases();
        iterator_t end = OpalData::getInstance()->getLastMaxPhases();
        for (; it < end; ++ it) {
            updateRFElement((*it).first, (*it).second);
174
        }
175
    }
176 177
}

178
void ParallelTTracker::execute() {
kraus's avatar
kraus committed
179
    Inform msg("ParallelTTracker ", *gmsg);
180
    OpalData::getInstance()->setInPrepState(true);
181

182 183
    BorisPusher pusher(itsReference);
    const double globalTimeShift = itsBunch_m->weHaveEnergyBins()? OpalData::getInstance()->getGlobalPhaseShift(): 0.0;
184
    OpalData::getInstance()->setGlobalPhaseShift(0.0);
185

186 187
    // the time step needs to be positive in the setup
    itsBunch_m->setdT(std::abs(itsBunch_m->getdT()));
188
    dtCurrentTrack_m = itsBunch_m->getdT();
189

190
    evenlyDistributeParticles();
191

192
    if (OpalData::getInstance()->hasPriorTrack() || OpalData::getInstance()->inRestartRun()) {
193
        OpalData::getInstance()->setOpenMode(OpalData::OPENMODE::APPEND);
194
    }
195

196
    prepareSections();
197

198
    double minTimeStep = stepSizes_m.getMinTimeStep();
199

200
    itsOpalBeamline_m.activateElements();
201

202 203
    if ( OpalData::getInstance()->hasPriorTrack() ||
         OpalData::getInstance()->inRestartRun()) {
204

205 206
        pathLength_m = itsBunch_m->get_sPos();
        zstart_m = pathLength_m;
207

208 209
        restoreCavityPhases();
    } else {
210

211 212 213 214 215 216
        double momentum = euclidean_norm(itsBunch_m->get_pmean_Distribution());
        CoordinateSystemTrafo beamlineToLab = itsOpalBeamline_m.getCSTrafoLab2Local().inverted();
        itsBunch_m->toLabTrafo_m = beamlineToLab;

        itsBunch_m->RefPartR_m = beamlineToLab.transformTo(Vector_t(0.0));
        itsBunch_m->RefPartP_m = beamlineToLab.rotateTo(momentum * Vector_t(0, 0, 1));
217

218 219
        if (itsBunch_m->getTotalNum() > 0) {
            if (!itsOpalBeamline_m.containsSource()) {
220 221
                momentum = itsReference.getP() / itsBunch_m->getM();
                itsBunch_m->RefPartP_m = beamlineToLab.rotateTo(momentum * Vector_t(0, 0, 1));
222
            }
223

224 225 226
            if (zstart_m > pathLength_m) {
                findStartPosition(pusher);
            }
227

228
            itsBunch_m->set_sPos(pathLength_m);
229 230 231
        }
    }

232 233 234 235 236 237 238 239 240
    stepSizes_m.advanceToPos(zstart_m);
    if (back_track) {
        itsBunch_m->setdT(-std::abs(itsBunch_m->getdT()));
        stepSizes_m.reverseDirection();
        if (pathLength_m < stepSizes_m.getZStop()) {
            ++ stepSizes_m;
        }
    }

241 242 243 244
    Vector_t rmin(0.0), rmax(0.0);
    if (itsBunch_m->getTotalNum() > 0) {
        itsBunch_m->get_bounds(rmin, rmax);
    }
245

246
    OrbitThreader oth(itsReference,
247 248
                      itsBunch_m->RefPartR_m,
                      itsBunch_m->RefPartP_m,
249 250 251
                      pathLength_m,
                      -rmin(2),
                      itsBunch_m->getT(),
252
                      (back_track? -minTimeStep: minTimeStep),
253
                      stepSizes_m,
254
                      itsOpalBeamline_m);
255

256
    oth.execute();
257

258
    saveCavityPhases();
259

260
    numParticlesInSimulation_m = itsBunch_m->getTotalNum();
261

262
    setTime();
263

264 265
    double time = itsBunch_m->getT() - globalTimeShift;
    itsBunch_m->setT(time);
266

267
    *gmsg << level1 << *itsBunch_m << endl;
268

269 270
    unsigned long long step = itsBunch_m->getGlobalTrackStep();
    OPALTimer::Timer myt1;
271
    *gmsg << "Track start at: " << myt1.time() << ", t= " << Util::getTimeString(time) << "; "
272 273
          << "zstart at: " << Util::getLengthString(pathLength_m)
          << endl;
274

275
    prepareEmission();
276

277 278
    *gmsg << level1
          << "Executing ParallelTTracker, initial dt= " << Util::getTimeString(itsBunch_m->getdT()) << ";\n"
279
          << "max integration steps " << stepSizes_m.getMaxSteps() << ", next step= " << step << endl;
280

281
    setOptionalVariables();
282

283 284 285 286
    globalEOL_m = false;
    wakeStatus_m = false;
    deletedParticles_m = false;
    OpalData::getInstance()->setInPrepState(false);
287 288 289 290 291 292 293 294 295 296
    while (!stepSizes_m.reachedEnd()) {
        unsigned long long trackSteps = stepSizes_m.getNumSteps() + step;
        dtCurrentTrack_m = stepSizes_m.getdT();
        changeDT(back_track);

        for (; step < trackSteps; ++ step) {
            Vector_t rmin(0.0), rmax(0.0);
            if (itsBunch_m->getTotalNum() > 0) {
                itsBunch_m->get_bounds(rmin, rmax);
            }
297

298
            timeIntegration1(pusher);
299

300 301
            itsBunch_m->Ef = Vector_t(0.0);
            itsBunch_m->Bf = Vector_t(0.0);
302

303
            computeSpaceChargeFields(step);
304

305
            selectDT(back_track);
306
            emitParticles(step);
307
            selectDT(back_track);
308

309
            computeExternalFields(oth);
310

311
            timeIntegration2(pusher);
312

313
            itsBunch_m->incrementT();
314

315 316
            if (itsBunch_m->getT() > 0.0 ||
                itsBunch_m->getdT() < 0.0) {
317
                updateReference(pusher);
318
            }
319

320 321 322
            if (deletedParticles_m) {
                evenlyDistributeParticles();
                deletedParticles_m = false;
323
            }
324
            itsBunch_m->set_sPos(pathLength_m);
325

326
            if (hasEndOfLineReached()) break;
327

328 329 330
            bool const psDump = ((itsBunch_m->getGlobalTrackStep() % Options::psDumpFreq) + 1 == Options::psDumpFreq);
            bool const statDump = ((itsBunch_m->getGlobalTrackStep() % Options::statDumpFreq) + 1 == Options::statDumpFreq);
            dumpStats(step, psDump, statDump);
331

332
            itsBunch_m->incTrackSteps();
333

334
            double beta = euclidean_norm(itsBunch_m->RefPartP_m / Util::getGamma(itsBunch_m->RefPartP_m));
335 336 337 338
            double driftPerTimeStep = std::abs(itsBunch_m->getdT()) * Physics::c * beta;
            if (std::abs(stepSizes_m.getZStop() - pathLength_m) < 0.5 * driftPerTimeStep) {
                break;
            }
339
        }
340

341 342
        if (globalEOL_m)
            break;
343

344
        ++ stepSizes_m;
345
    }
346

347
    itsBunch_m->set_sPos(pathLength_m);
348

349 350 351
    if (numParticlesInSimulation_m > minBinEmitted_m) {
        numParticlesInSimulation_m = itsBunch_m->getTotalNum();
    }
352

353 354 355
    bool const psDump = (((itsBunch_m->getGlobalTrackStep() - 1) % Options::psDumpFreq) + 1 != Options::psDumpFreq);
    bool const statDump = (((itsBunch_m->getGlobalTrackStep() - 1) % Options::statDumpFreq) + 1 != Options::statDumpFreq);
    writePhaseSpace((step + 1), psDump, statDump);
356

357
    msg << level2 << "Dump phase space of last step" << endl;
358

359
    itsOpalBeamline_m.switchElementsOff();
360

361 362
    OPALTimer::Timer myt3;
    *gmsg << "done executing ParallelTTracker at " << myt3.time() << endl;
363

364
    Monitor::writeStatistics();
gsell's avatar
gsell committed
365

366
    OpalData::getInstance()->setPriorTrack();
367 368 369 370 371 372
}

void ParallelTTracker::prepareSections() {

    itsBeamline_m.accept(*this);
    itsOpalBeamline_m.prepareSections();
373 374 375 376

    itsOpalBeamline_m.compute3DLattice();
    itsOpalBeamline_m.save3DLattice();
    itsOpalBeamline_m.save3DInput();
377 378
}

379
void ParallelTTracker::timeIntegration1(BorisPusher & pusher) {
380

381 382 383
    IpplTimings::startTimer(timeIntegrationTimer1_m);
    pushParticles(pusher);
    IpplTimings::stopTimer(timeIntegrationTimer1_m);
384 385
}

386

387
void ParallelTTracker::timeIntegration2(BorisPusher & pusher) {
388
    /*
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403
      transport and emit particles
      that passed the cathode in the first
      half-step or that would pass it in the
      second half-step.

      to make IPPL and the field solver happy
      make sure that at least 10 particles are emitted

      also remember that node 0 has
      all the particles to be emitted

      this has to be done *after* the calculation of the
      space charges! thereby we neglect space charge effects
      in the very first step of a new-born particle.

404
    */
405

406
    IpplTimings::startTimer(timeIntegrationTimer2_m);
407 408 409
    kickParticles(pusher);
    //switchElements();
    pushParticles(pusher);
410

411 412 413
    const unsigned int localNum = itsBunch_m->getLocalNum();
    for (unsigned int i = 0; i < localNum; ++ i) {
        itsBunch_m->dt[i] = itsBunch_m->getdT();
414
    }
415

416 417 418
    IpplTimings::stopTimer(timeIntegrationTimer2_m);
}

419
void ParallelTTracker::selectDT(bool backTrack) {
420

421 422 423
    if (itsBunch_m->getIfBeamEmitting()) {
        double dt = itsBunch_m->getEmissionDeltaT();
        itsBunch_m->setdT(dt);
424 425
    } else {
        double dt = dtCurrentTrack_m;
426
        itsBunch_m->setdT(dt);
gsell's avatar
gsell committed
427
    }
428 429 430
    if (backTrack) {
        itsBunch_m->setdT(-std::abs(itsBunch_m->getdT()));
    }
431
}
gsell's avatar
gsell committed
432

433 434
void ParallelTTracker::changeDT(bool backTrack) {
    selectDT(backTrack);
435 436 437
    const unsigned int localNum = itsBunch_m->getLocalNum();
    for (unsigned int i = 0; i < localNum; ++ i) {
        itsBunch_m->dt[i] = itsBunch_m->getdT();
438 439
    }
}
440 441

void ParallelTTracker::emitParticles(long long step) {
442 443 444 445
    if (!itsBunch_m->weHaveEnergyBins()) return;

    if (itsBunch_m->getIfBeamEmitting()) {
        CoordinateSystemTrafo gunToLab = itsOpalBeamline_m.getCSTrafoLab2Local().inverted();
446
        CoordinateSystemTrafo refToGun = itsOpalBeamline_m.getCSTrafoLab2Local() * itsBunch_m->toLabTrafo_m;
447

448
        transformBunch(refToGun);
449

450
        itsBunch_m->switchToUnitlessPositions(true);
451 452 453

        Vector_t externalE = Vector_t(0.0);
        Vector_t externalB = Vector_t(0.0);
454 455 456
        itsOpalBeamline_m.getFieldAt(gunToLab.transformTo(Vector_t(0.0)),
                                     gunToLab.rotateTo(Vector_t(0.0)),
                                     itsBunch_m->getT() + 0.5 * itsBunch_m->getdT(),
457 458
                                     externalE,
                                     externalB);
459
        itsBunch_m->emitParticles(externalE(2));
460

461 462 463
        itsBunch_m->updateNumTotal();
        numParticlesInSimulation_m = itsBunch_m->getTotalNum();
        itsBunch_m->switchOffUnitlessPositions(true);
464

465 466
        transformBunch(refToGun.inverted());
    }
467

468 469 470
    if (step > minStepforReBin_m) {
        itsBunch_m->Rebin();
        itsBunch_m->resetInterpolationCache(true);
gsell's avatar
gsell committed
471
    }
472
}
473

adelmann's avatar
adelmann committed
474

475 476
void ParallelTTracker::computeSpaceChargeFields(unsigned long long step) {
    if (numParticlesInSimulation_m <= minBinEmitted_m) return;
477

478
    if (!itsBunch_m->hasFieldSolver()) return;
gsell's avatar
gsell committed
479

480 481 482 483
    itsBunch_m->calcBeamParameters();
    Quaternion alignment = getQuaternion(itsBunch_m->get_pmean(), Vector_t(0, 0, 1));
    CoordinateSystemTrafo beamToReferenceCSTrafo(Vector_t(0, 0, pathLength_m), alignment.conjugate());
    CoordinateSystemTrafo referenceToBeamCSTrafo = beamToReferenceCSTrafo.inverted();
gsell's avatar
gsell committed
484

485 486 487 488 489 490
    const unsigned int localNum1 = itsBunch_m->getLocalNum();
    for (unsigned int i = 0; i < localNum1; ++ i) {
        itsBunch_m->R[i] = referenceToBeamCSTrafo.transformTo(itsBunch_m->R[i]);
    }

    itsBunch_m->boundp();
491

492 493 494
    if (step % repartFreq_m + 1 == repartFreq_m) {
        doBinaryRepartition();
    }
495

496 497 498 499 500 501
    if (itsBunch_m->weHaveEnergyBins()) {
        itsBunch_m->calcGammas();
        itsBunch_m->resetInterpolationCache();
        ParticleAttrib<double> Q_back = itsBunch_m->Q;
        for (int binNumber = 0; binNumber < itsBunch_m->getLastEmittedEnergyBin() &&
                 binNumber < itsBunch_m->getNumberOfEnergyBins(); ++binNumber) {
502

503 504 505 506
            itsBunch_m->setBinCharge(binNumber);
            itsBunch_m->setGlobalMeanR(itsBunch_m->get_centroid());
            itsBunch_m->computeSelfFields(binNumber);
            itsBunch_m->Q = Q_back;
507

508
        }
509

510
    } else {
511 512
        itsBunch_m->setGlobalMeanR(itsBunch_m->get_centroid());
        itsBunch_m->computeSelfFields();
gsell's avatar
gsell committed
513
    }
514

515 516 517 518 519 520
    const unsigned int localNum2 = itsBunch_m->getLocalNum();
    for (unsigned int i = 0; i < localNum2; ++ i) {
        itsBunch_m->R[i] = beamToReferenceCSTrafo.transformTo(itsBunch_m->R[i]);
        itsBunch_m->Ef[i] = beamToReferenceCSTrafo.rotateTo(itsBunch_m->Ef[i]);
        itsBunch_m->Bf[i] = beamToReferenceCSTrafo.rotateTo(itsBunch_m->Bf[i]);
    }
521
}
gsell's avatar
gsell committed
522 523


524 525
void ParallelTTracker::computeExternalFields(OrbitThreader &oth) {
    IpplTimings::startTimer(fieldEvaluationTimer_m);
kraus's avatar
kraus committed
526
    Inform msg("ParallelTTracker ", *gmsg);
527
    const unsigned int localNum = itsBunch_m->getLocalNum();
528
    bool locPartOutOfBounds = false, globPartOutOfBounds = false;
529 530 531
    Vector_t rmin(0.0), rmax(0.0);
    if (itsBunch_m->getTotalNum() > 0)
        itsBunch_m->get_bounds(rmin, rmax);
532
    IndexMap::value_t elements;
533

534 535
    try {
        elements = oth.query(pathLength_m + 0.5 * (rmax(2) + rmin(2)), rmax(2) - rmin(2));
snuverink_j's avatar
snuverink_j committed
536
    } catch(IndexMap::OutOfBounds &e) {
537 538 539
        globalEOL_m = true;
        return;
    }
540

541 542
    IndexMap::value_t::const_iterator it = elements.begin();
    const IndexMap::value_t::const_iterator end = elements.end();
543

544 545
    for (; it != end; ++ it) {
        CoordinateSystemTrafo refToLocalCSTrafo = (itsOpalBeamline_m.getMisalignment((*it)) *
546
                                                   (itsOpalBeamline_m.getCSTrafoLab2Local((*it)) * itsBunch_m->toLabTrafo_m));
547
        CoordinateSystemTrafo localToRefCSTrafo = refToLocalCSTrafo.inverted();
548

549
        (*it)->setCurrentSCoordinate(pathLength_m + rmin(2));
550

551 552
        for (unsigned int i = 0; i < localNum; ++ i) {
            if (itsBunch_m->Bin[i] < 0) continue;
553

554 555 556
            itsBunch_m->R[i] = refToLocalCSTrafo.transformTo(itsBunch_m->R[i]);
            itsBunch_m->P[i] = refToLocalCSTrafo.rotateTo(itsBunch_m->P[i]);
            double &dt = itsBunch_m->dt[i];
557

558
            Vector_t localE(0.0), localB(0.0);
559

560 561 562 563
            if ((*it)->apply(i, itsBunch_m->getT() + 0.5 * dt, localE, localB)) {
                itsBunch_m->R[i] = localToRefCSTrafo.transformTo(itsBunch_m->R[i]);
                itsBunch_m->P[i] = localToRefCSTrafo.rotateTo(itsBunch_m->P[i]);
                itsBunch_m->Bin[i] = -1;
564 565
                locPartOutOfBounds = true;

566
                continue;
567
            }
568

569 570 571 572
            itsBunch_m->R[i] = localToRefCSTrafo.transformTo(itsBunch_m->R[i]);
            itsBunch_m->P[i] = localToRefCSTrafo.rotateTo(itsBunch_m->P[i]);
            itsBunch_m->Ef[i] += localToRefCSTrafo.rotateTo(localE);
            itsBunch_m->Bf[i] += localToRefCSTrafo.rotateTo(localB);
573
        }
574 575 576
    }

    IpplTimings::stopTimer(fieldEvaluationTimer_m);
577

578
    computeWakefield(elements);
579 580 581
#ifdef ENABLE_OPAL_FEL
    computeUndulator(elements);
#endif
582 583
    computeParticleMatterInteraction(elements, oth);

584 585
    reduce(locPartOutOfBounds, globPartOutOfBounds, OpOrAssign());

586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
    size_t ne = 0;
    if (globPartOutOfBounds) {
        if (itsBunch_m->hasFieldSolver()) {
            ne = itsBunch_m->boundp_destroyT();
        } else {
            ne = itsBunch_m->destroyT();
        }
        numParticlesInSimulation_m  = itsBunch_m->getTotalNum();
        deletedParticles_m = true;
    }

    size_t totalNum = itsBunch_m->getTotalNum();
    if (numParticlesInSimulation_m > minBinEmitted_m || totalNum > minBinEmitted_m) {
        numParticlesInSimulation_m = totalNum;
    }

    if (ne > 0) {
        msg << level1 << "* Deleted " << ne << " particles, "
604
            << "remaining " << numParticlesInSimulation_m << " particles" << endl;
605 606 607
    }
}

608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631
#ifdef ENABLE_OPAL_FEL
void ParallelTTracker::computeUndulator(IndexMap::value_t &elements) {
    // Check if bunch has entered undulator field.
    UndulatorRep* und;
    IndexMap::value_t::const_iterator it = elements.begin();
    for (; it != elements.end(); ++ it)
        if ((*it)->getType() == ElementBase::UNDULATOR) {
            und = dynamic_cast<UndulatorRep*>(it->get());
            if (!und->getHasBeenSimulated())
                break;
        }
    if (it == elements.end())
        return;

    // Apply MITHRA full wave solver for undulator.
    CoordinateSystemTrafo refToLocalCSTrafo = (itsOpalBeamline_m.getMisalignment((*it)) *
                                               (itsOpalBeamline_m.getCSTrafoLab2Local((*it)) * itsBunch_m->toLabTrafo_m));
    
    und->apply(itsBunch_m, refToLocalCSTrafo);

    evenlyDistributeParticles();
}
#endif

632 633 634 635 636 637 638 639 640 641 642 643
void ParallelTTracker::computeWakefield(IndexMap::value_t &elements) {
    bool hasWake = false;
    WakeFunction *wfInstance;

    Inform msg("ParallelTTracker ", *gmsg);

    const unsigned int localNum = itsBunch_m->getLocalNum();

    IndexMap::value_t::const_iterator it = elements.begin();
    const IndexMap::value_t::const_iterator end = elements.end();

    for (; it != end; ++ it) {
644 645
        if ((*it)->hasWake() && !hasWake) {
            IpplTimings::startTimer(WakeFieldTimer_m);
646

647
            hasWake = true;
648

649 650 651 652 653 654 655 656 657 658 659
            if ((*it)->getWake()->getType() == "CSRWakeFunction" ||
                (*it)->getWake()->getType() == "CSRIGFWakeFunction") {
                if ((*it)->getType() == ElementBase::RBEND ||
                    (*it)->getType() == ElementBase::SBEND) {
                    wfInstance = (*it)->getWake();
                    wakeFunction_m = wfInstance;
                } else {
                    wfInstance = wakeFunction_m;
                }
            } else {
                wfInstance = (*it)->getWake();
660
            }
661

662
            if (!wfInstance) {
663
                throw OpalException("ParallelTTracker::computeWakefield",
664
                                    "empty wake function");
665
            }
666 667 668 669 670

            if (!wakeStatus_m) {
                msg << level2 << "============== START WAKE CALCULATION =============" << endl;
                wfInstance->initialize((*it).get());
                wakeStatus_m = true;
671 672
            }

673
            if (!itsBunch_m->hasFieldSolver()) itsBunch_m->calcBeamParameters();
674

675 676 677
            Quaternion alignment = getQuaternion(itsBunch_m->get_pmean(), Vector_t(0, 0, 1));
            CoordinateSystemTrafo referenceToBeamCSTrafo(Vector_t(0.0), alignment);
            CoordinateSystemTrafo beamToReferenceCSTrafo = referenceToBeamCSTrafo.inverted();
678

679 680
            for (unsigned int i = 0; i < localNum; ++ i) {
                itsBunch_m->R[i] = referenceToBeamCSTrafo.transformTo(itsBunch_m->R[i]);
681
                itsBunch_m->P[i] = referenceToBeamCSTrafo.rotateTo(itsBunch_m->P[i]);
682
                itsBunch_m->Ef[i] = referenceToBeamCSTrafo.rotateTo(itsBunch_m->Ef[i]);
683
            }
684

frey_m's avatar
frey_m committed
685
            wfInstance->apply(itsBunch_m);
686

687 688
            for (unsigned int i = 0; i < localNum; ++ i) {
                itsBunch_m->R[i] = beamToReferenceCSTrafo.transformTo(itsBunch_m->R[i]);
689
                itsBunch_m->P[i] = beamToReferenceCSTrafo.rotateTo(itsBunch_m->P[i]);
690
                itsBunch_m->Ef[i] = beamToReferenceCSTrafo.rotateTo(itsBunch_m->Ef[i]);
691
            }
692 693

            IpplTimings::stopTimer(WakeFieldTimer_m);
694
        }
695
    }
696

697 698 699 700
    if (wakeStatus_m && !hasWake) {
        msg << level2 << "=============== END WAKE CALCULATION ==============" << endl;
        wakeStatus_m = false;
    }
701
}
gsell's avatar
gsell committed
702

703 704
void ParallelTTracker::computeParticleMatterInteraction(IndexMap::value_t elements, OrbitThreader &oth) {
    Inform msg("ParallelTTracker ", *gmsg);
705
    std::set<IndexMap::value_t::value_type> elementsWithParticleMatterInteraction;
snuverink_j's avatar
snuverink_j committed
706
    std::set<ParticleMatterInteractionHandler*> particleMatterinteractionHandlers;
707
    IndexMap::key_t currentRange{0.0, 0.0};
708 709 710

    while (elements.size() > 0) {
        auto it = elements.begin();
711 712
        if ((*it)->hasParticleMatterInteraction()) {
            elementsWithParticleMatterInteraction.insert(*it);
snuverink_j's avatar
snuverink_j committed
713
            particleMatterinteractionHandlers.insert((*it)->getParticleMatterInteraction());
714

715 716 717
            IndexMap::key_t range = oth.getRange(*it, pathLength_m);
            currentRange.begin = std::min(currentRange.begin, range.begin);
            currentRange.end = std::max(currentRange.end, range.end);
718 719 720

            IndexMap::value_t touching = oth.getTouchingElements(range);
            elements.insert(touching.begin(), touching.end());
721
        }
722 723

        elements.erase(it);
724
    }
725

726 727 728 729
    if (elementsWithParticleMatterInteraction.size() > 0) {
        std::set<ParticleMatterInteractionHandler*> oldSPHandlers;
        std::vector<ParticleMatterInteractionHandler*> leftBehindSPHandlers, newSPHandlers;
        for (auto it: activeParticleMatterInteractionHandlers_m) {
730 731 732 733
            oldSPHandlers.insert(it);
        }

        leftBehindSPHandlers.resize(std::max(oldSPHandlers.size(),
snuverink_j's avatar
snuverink_j committed
734
                                             particleMatterinteractionHandlers.size()));
735
        auto last = std::set_difference(oldSPHandlers.begin(), oldSPHandlers.end(),
snuverink_j's avatar
snuverink_j committed
736
                                        particleMatterinteractionHandlers.begin(), particleMatterinteractionHandlers.end(),
737 738 739 740 741
                                        leftBehindSPHandlers.begin());
        leftBehindSPHandlers.resize(last - leftBehindSPHandlers.begin());

        for (auto it: leftBehindSPHandlers) {
            if (!it->stillActive()) {
742
                activeParticleMatterInteractionHandlers_m.erase(it);
743 744 745 746
            }
        }

        newSPHandlers.resize(std::max(oldSPHandlers.size(),
747
                                      elementsWithParticleMatterInteraction.size()));
snuverink_j's avatar
snuverink_j committed
748
        last = std::set_difference(particleMatterinteractionHandlers.begin(), particleMatterinteractionHandlers.end(),
749 750 751 752 753
                                   oldSPHandlers.begin(), oldSPHandlers.end(),
                                   newSPHandlers.begin());
        newSPHandlers.resize(last - newSPHandlers.begin());

        for (auto it: newSPHandlers) {
754
            activeParticleMatterInteractionHandlers_m.insert(it);
755 756
        }

757 758 759
        if(!particleMatterStatus_m) {
            msg << level2 << "============== START PARTICLE MATTER INTERACTION CALCULATION =============" << endl;
            particleMatterStatus_m = true;
760
        }
761
    }
762

763
    if (particleMatterStatus_m) {
764 765 766
        do {
            ///all particles in material if max per node is 2 and other degraders have 0 particles
            //check if more than one degrader has particles
767
            ParticleMatterInteractionHandler* onlyDegraderWithParticles = NULL;
768
            int degradersWithParticlesCount = 0;
769
            for (auto it: activeParticleMatterInteractionHandlers_m) {
770 771 772 773 774 775 776