// // Class BeamStripping // Defines the abstract interface environment for // beam stripping physics. // // Copyright (c) 2018-2021, Pedro Calvo, CIEMAT, Spain // All rights reserved // // Implemented as part of the PhD thesis // "Optimizing the radioisotope production of the novel AMIT // superconducting weak focusing cyclotron" // // 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 . // #include "AbsBeamline/BeamStripping.h" #include "AbsBeamline/BeamlineVisitor.h" #include "AbsBeamline/Cyclotron.h" #include "Algorithms/PartBunchBase.h" #include "Fields/Fieldmap.h" #include "Physics/Physics.h" #include "Solvers/BeamStrippingPhysics.hh" #include "Solvers/ParticleMatterInteractionHandler.hh" #include "Utilities/LogicalError.h" #include "Utilities/GeneralClassicException.h" #include #include #include #include #include "Utility/Inform.h" #define CHECK_BSTP_FSCANF_EOF(arg) if (arg == EOF)\ throw GeneralClassicException("BeamStripping::getPressureFromFile",\ "fscanf returned EOF at " #arg); extern Inform* gmsg; BeamStripping::BeamStripping(): BeamStripping("") {} BeamStripping::BeamStripping(const BeamStripping& right): Component(right), gas_m(right.gas_m), pressure_m(right.pressure_m), pmapfn_m(right.pmapfn_m), pscale_m(right.pscale_m), temperature_m(right.temperature_m), stop_m(right.stop_m), minr_m(right.minr_m), maxr_m(right.maxr_m), minz_m(right.minz_m), maxz_m(right.maxz_m), parmatint_m(NULL) { } BeamStripping::BeamStripping(const std::string& name): Component(name), gas_m(ResidualGas::NOGAS), pressure_m(0.0), pmapfn_m(""), pscale_m(0.0), temperature_m(0.0), stop_m(true), minr_m(0.0), maxr_m(0.0), minz_m(0.0), maxz_m(0.0), parmatint_m(NULL) { } BeamStripping::~BeamStripping() { if (online_m) goOffline(); } void BeamStripping::accept(BeamlineVisitor& visitor) const { visitor.visitBeamStripping(*this); } void BeamStripping::setPressure(double pressure) { pressure_m = pressure; } double BeamStripping::getPressure() const { if (pressure_m > 0.0) return pressure_m; else { throw LogicalError("BeamStripping::getPressure", "Pressure must not be zero"); } } void BeamStripping::setTemperature(double temperature) { temperature_m = temperature; } double BeamStripping::getTemperature() const { if (temperature_m > 0.0) return temperature_m; else { throw LogicalError("BeamStripping::getTemperature", "Temperature must not be zero"); } } void BeamStripping::setPressureMapFN(std::string p) { pmapfn_m = p; } std::string BeamStripping::getPressureMapFN() const { return pmapfn_m; } void BeamStripping::setPScale(double ps) { pscale_m = ps; } double BeamStripping::getPScale() const { if (pscale_m > 0.0) return pscale_m; else { throw LogicalError("BeamStripping::getPScale", "PScale must be positive"); } } void BeamStripping::setResidualGas(std::string gas) { if (gas == "AIR") { gas_m = ResidualGas::AIR; } else if (gas == "H2") { gas_m = ResidualGas::H2; } } ResidualGas BeamStripping::getResidualGas() const { return gas_m; } std::string BeamStripping::getResidualGasName() { switch (gas_m) { case ResidualGas::AIR: return "AIR"; case ResidualGas::H2: return "H2"; default: throw GeneralClassicException("BeamStripping::getResidualGasName", "Residual gas not found"); } } void BeamStripping::setStop(bool stopflag) { stop_m = stopflag; } bool BeamStripping::getStop() const { return stop_m; } bool BeamStripping::checkBeamStripping(PartBunchBase* bunch, Cyclotron* cycl) { bool flagNeedUpdate = false; Vector_t rmin, rmax; bunch->get_bounds(rmin, rmax); std::pair boundingSphere; boundingSphere.first = 0.5 * (rmax + rmin); boundingSphere.second = euclidean_norm(rmax - boundingSphere.first); maxr_m = cycl->getMaxR(); minr_m = cycl->getMinR(); maxz_m = cycl->getMaxZ(); minz_m = cycl->getMinZ(); size_t tempnum = bunch->getLocalNum(); for (unsigned int i = 0; i < tempnum; ++i) { int pflag = checkPoint(bunch->R[i](0), bunch->R[i](1), bunch->R[i](2)); if ( (pflag != 0) && (bunch->Bin[i] != -1) ) flagNeedUpdate = true; } reduce(&flagNeedUpdate, &flagNeedUpdate + 1, &flagNeedUpdate, OpBitwiseOrAssign()); if (flagNeedUpdate && parmatint_m) { dynamic_cast(parmatint_m)->setCyclotron(cycl); parmatint_m->apply(bunch, boundingSphere); } return flagNeedUpdate; } void BeamStripping::initialise(PartBunchBase* bunch, double& startField, double& endField) { endField = startField + getElementLength(); initialise(bunch, pscale_m); } void BeamStripping::initialise(PartBunchBase* bunch, const double& scaleFactor) { RefPartBunch_m = bunch; parmatint_m = getParticleMatterInteraction(); if (pmapfn_m != "") { getPressureFromFile(scaleFactor); // calculate the radii of initial grid. initR(PP.rmin, PP.delr, PField.nrad); } goOnline(-1e6); // change the mass and charge to simulate real particles for (size_t i = 0; i < bunch->getLocalNum(); ++i) { bunch->M[i] = bunch->getM()*1E-9; bunch->Q[i] = bunch->getQ() * Physics::q_e; if (bunch->weHaveBins()) bunch->Bin[bunch->getLocalNum()-1] = bunch->Bin[i]; } } void BeamStripping::finalise() { if (online_m) goOffline(); *gmsg << "* Finalize Beam Stripping" << endl; } void BeamStripping::goOnline(const double &) { } void BeamStripping::goOffline() { online_m = false; } bool BeamStripping::bends() const { return false; } void BeamStripping::getDimensions(double& /*zBegin*/, double& /*zEnd*/) const {} ElementBase::ElementType BeamStripping::getType() const { return BEAMSTRIPPING; } std::string BeamStripping::getBeamStrippingShape() { return "BeamStripping"; } int BeamStripping::checkPoint(const double& x, const double& y, const double& z) { int cn; double rpos = std::sqrt(x * x + y * y); if (z >= maxz_m || z <= minz_m || rpos >= maxr_m || rpos <= minr_m) { cn = 0; } else { cn = 1; } return (cn); // 0 if out, and 1 if in } double BeamStripping::checkPressure(const double& x, const double& y) { double pressure = 0.0; if (pmapfn_m != "") { const double rad = std::sqrt(x * x + y * y); const double xir = (rad - PP.rmin) / PP.delr; // ir : the number of path whose radius is less than the 4 points of cell which surround the particle. const int ir = (int)xir; // wr1 : the relative distance to the inner path radius const double wr1 = xir - (double)ir; // wr2 : the relative distance to the outer path radius const double wr2 = 1.0 - wr1; const double tempv = std::atan(y / x); double tet = tempv; if ((x < 0) && (y >= 0)) tet = Physics::pi + tempv; else if ((x < 0) && (y <= 0)) tet = Physics::pi + tempv; else if ((x > 0) && (y <= 0)) tet = Physics::two_pi + tempv; else if ((x == 0) && (y > 0)) tet = Physics::pi / 2.0; else if ((x == 0) && (y < 0)) tet = 1.5 * Physics::pi; // the actual angle of particle tet = tet * Physics::rad2deg; // the corresponding angle on the field map // Note: this does not work if the start point of field map does not equal zero. double xit = tet / PP.dtet; int it = (int) xit; const double wt1 = xit - (double)it; const double wt2 = 1.0 - wt1; // it : the number of point on the inner path whose angle is less than the particle' corresponding angle. // include zero degree point it++; double epsilon = 0.06; if (tet > 360 - epsilon && tet < 360 + epsilon) it = 0; int r1t1, r2t1, r1t2, r2t2; // r1t1 : the index of the "min angle, min radius" point in the 2D field array. // considering the array start with index of zero, minus 1. r1t1 = idx(ir, it); r2t1 = idx(ir + 1, it); r1t2 = idx(ir, it + 1); r2t2 = idx(ir + 1, it + 1); if ((it >= 0) && (ir >= 0) && (it < PField.ntetS) && (ir < PField.nrad)) { pressure = (PField.pfld[r1t1] * wr2 * wt2 + PField.pfld[r2t1] * wr1 * wt2 + PField.pfld[r1t2] * wr2 * wt1 + PField.pfld[r2t2] * wr1 * wt1); if (pressure <= 0.0) { *gmsg << level4 << getName() << ": Pressure data from file zero." << endl; *gmsg << level4 << getName() << ": Take constant value through BeamStripping::getPressure" << endl; pressure = getPressure(); } } else if (ir >= PField.nrad) { *gmsg << level4 << getName() << ": Particle out of maximum radial position of pressure field map." << endl; *gmsg << level4 << getName() << ": Take constant value through BeamStripping::getPressure" << endl; pressure = getPressure(); } else { throw GeneralClassicException("BeamStripping::checkPressure", "Pressure data not found"); } } else { pressure = getPressure(); } return pressure; } // Calculates radius of initial grid (dimensions in [m]!) void BeamStripping::initR(double rmin, double dr, int nrad) { PP.rarr.resize(nrad); for (int i = 0; i < nrad; i++) PP.rarr[i] = rmin + i * dr; PP.delr = dr; } // Read pressure map from external file. void BeamStripping::getPressureFromFile(const double& scaleFactor) { FILE* f = NULL; *gmsg << "* " << endl; *gmsg << "* Reading pressure field map " << pmapfn_m << endl; PP.Pfact = scaleFactor; if ((f = std::fopen(pmapfn_m.c_str(), "r")) == NULL) { throw GeneralClassicException("BeamStripping::getPressureFromFile", "failed to open file '" + pmapfn_m + "', please check if it exists"); } CHECK_BSTP_FSCANF_EOF(std::fscanf(f, "%lf", &PP.rmin)); *gmsg << "* --- Minimal radius of measured pressure map: " << PP.rmin << " [mm]" << endl; CHECK_BSTP_FSCANF_EOF(std::fscanf(f, "%lf", &PP.delr)); //if the value is negative, the actual value is its reciprocal. if (PP.delr < 0.0) PP.delr = 1.0 / (-PP.delr); *gmsg << "* --- Stepsize in radial direction: " << PP.delr << " [mm]" << endl; CHECK_BSTP_FSCANF_EOF(std::fscanf(f, "%lf", &PP.tetmin)); *gmsg << "* --- Minimal angle of measured pressure map: " << PP.tetmin << " [deg]" << endl; CHECK_BSTP_FSCANF_EOF(std::fscanf(f, "%lf", &PP.dtet)); //if the value is negative, the actual value is its reciprocal. if (PP.dtet < 0.0) PP.dtet = 1.0 / (-PP.dtet); *gmsg << "* --- Stepsize in azimuthal direction: " << PP.dtet << " [deg]" << endl; CHECK_BSTP_FSCANF_EOF(std::fscanf(f, "%d", &PField.ntet)); *gmsg << "* --- Grid points along azimuth (ntet): " << PField.ntet << endl; CHECK_BSTP_FSCANF_EOF(std::fscanf(f, "%d", &PField.nrad)); *gmsg << "* --- Grid points along radius (nrad): " << PField.nrad << endl; *gmsg << "* --- Maximum radial position: " << PP.rmin + (PField.nrad-1)*PP.delr << " [mm]" << endl; PP.rmin *= 0.001; // mm --> m PP.delr *= 0.001; // mm --> m PField.ntetS = PField.ntet + 1; *gmsg << "* --- Adding a guard cell along azimuth" << endl; PField.ntot = PField.nrad * PField.ntetS; PField.pfld.resize(PField.ntot); *gmsg << "* --- Total stored grid point number ((ntet+1) * nrad) : " << PField.ntot << endl; *gmsg << "* --- Escale factor: " << PP.Pfact << endl; for (int i = 0; i < PField.nrad; i++) { for (int k = 0; k < PField.ntet; k++) { CHECK_BSTP_FSCANF_EOF(std::fscanf(f, "%16lE", &(PField.pfld[idx(i, k)]))); PField.pfld[idx(i, k)] *= PP.Pfact; } } *gmsg << "*" << endl; std::fclose(f); } #undef CHECK_BSTP_FSCANF_EOF