File indexing completed on 2025-02-23 09:22:20
0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017
0018
0019
0020
0021
0022
0023
0024
0025
0026
0027
0028
0029
0030 #include "RBE.hh"
0031
0032 #include "G4Pow.hh"
0033 #include "G4SystemOfUnits.hh"
0034 #include "G4UnitsTable.hh"
0035
0036 #include "RBEAccumulable.hh"
0037 #include "RBEMessenger.hh"
0038 #include "VoxelizedSensitiveDetector.hh"
0039
0040
0041
0042
0043 #include "Dose.hh"
0044 #include "Manager.hh"
0045 #include <G4NistManager.hh>
0046
0047 #include <algorithm>
0048 #include <cstdlib>
0049 #include <fstream>
0050 #include <iomanip>
0051 #include <iostream>
0052 #include <numeric>
0053 #include <sstream>
0054
0055 #define width 15L
0056
0057 namespace RadioBio
0058 {
0059
0060
0061
0062 RBE::RBE() : VRadiobiologicalQuantity()
0063 {
0064 fPath = "RadioBio";
0065
0066
0067 fMessenger = new RBEMessenger(this);
0068
0069 Initialize();
0070 }
0071
0072
0073
0074 RBE::~RBE()
0075 {
0076 delete fMessenger;
0077 }
0078
0079
0080
0081 void RBE::Initialize()
0082 {
0083 fLnS.resize(VoxelizedSensitiveDetector::GetInstance()->GetTotalVoxelNumber());
0084 fDoseX.resize(VoxelizedSensitiveDetector::GetInstance()->GetTotalVoxelNumber());
0085
0086 fInitialized = true;
0087 }
0088
0089
0090
0091 void RBE::Store()
0092 {
0093 StoreAlphaAndBeta();
0094 StoreRBE();
0095 }
0096
0097
0098
0099 void RBE::PrintParameters()
0100 {
0101 G4cout << "*******************************************" << G4endl
0102 << "******* Parameters of the class RBE *******" << G4endl
0103 << "*******************************************" << G4endl;
0104 PrintVirtualParameters();
0105 G4cout << "** RBE Cell line: " << fActiveCellLine << G4endl;
0106 G4cout << "** RBE Dose threshold value: " << fDoseCut / gray << " gray" << G4endl;
0107 G4cout << "** RBE Alpha X value: " << fAlphaX * gray << " 1/gray" << G4endl;
0108 G4cout << "** RBE Beta X value: " << fBetaX * std::pow(gray, 2.0) << " 1/gray2" << G4endl;
0109 G4cout << "*******************************************" << G4endl;
0110 }
0111
0112
0113
0114
0115
0116
0117
0118
0119
0120
0121
0122
0123
0124 std::vector<G4String> split(const G4String& line, const G4String& delimiter)
0125 {
0126 std::vector<G4String> result;
0127
0128 size_t current = 0;
0129 size_t pos = 0;
0130
0131 while (pos != G4String::npos) {
0132 pos = line.find(delimiter, current);
0133 G4String token = line.substr(current, pos - current);
0134
0135 G4StrUtil::strip(token);
0136 G4StrUtil::strip(token, '\"');
0137
0138 result.push_back(token);
0139 current = pos + delimiter.size();
0140 }
0141 return result;
0142 }
0143
0144
0145
0146 void RBE::LoadLEMTable(G4String path)
0147 {
0148 std::ifstream in(path);
0149 if (!in) {
0150 std::stringstream ss;
0151 ss << "Cannot open LEM table input file: " << path;
0152 G4Exception("RBE::LoadData", "WrongTable", FatalException, ss.str().c_str());
0153 }
0154
0155
0156 G4String line;
0157 if (!getline(in, line)) {
0158 std::stringstream ss;
0159 ss << "Cannot read header from the LEM table file: " << path;
0160 G4Exception("RBE::LoadLEMTable", "CannotReadHeader", FatalException, ss.str().c_str());
0161 }
0162 std::vector<G4String> header = split(line, ",");
0163
0164
0165 std::vector<G4String> columns = {"alpha_x", "beta_x", "D_t", "specific_energy",
0166 "alpha", "beta", "cell", "particle"};
0167 std::map<G4String, int> columnIndices;
0168 for (auto columnName : columns) {
0169 auto pos = find(header.begin(), header.end(), columnName);
0170 if (pos == header.end()) {
0171 std::stringstream ss;
0172 ss << "Column " << columnName << " not present in the LEM table file.";
0173 G4Exception("RBE::LoadLEMTable", "ColumnNotPresent", FatalException, ss.str().c_str());
0174 }
0175 else {
0176 columnIndices[columnName] = distance(header.begin(), pos);
0177 }
0178 }
0179
0180
0181 while (getline(in, line)) {
0182 std::vector<G4String> lineParts = split(line, ",");
0183 G4String cellLine = lineParts[columnIndices["cell"]];
0184
0185 G4NistManager* man = G4NistManager::Instance();
0186 G4int element = man->FindOrBuildElement(lineParts[columnIndices["particle"]])->GetZasInt();
0187
0188 fTablesEnergy[cellLine][element].push_back(
0189 std::stod(lineParts[columnIndices["specific_energy"]]) * MeV);
0190 fTablesAlpha[cellLine][element].push_back(stod(lineParts[columnIndices["alpha"]]));
0191
0192
0193
0194 fTablesBeta[cellLine][element].push_back(stod(lineParts[columnIndices["beta"]]));
0195
0196 fTablesAlphaX[cellLine] = stod(lineParts[columnIndices["alpha_x"]]) / gray;
0197 fTablesBetaX[cellLine] = stod(lineParts[columnIndices["beta_x"]]) / (gray * gray);
0198 fTablesDoseCut[cellLine] = stod(lineParts[columnIndices["D_t"]]) * gray;
0199 }
0200
0201
0202
0203 for (auto aPair : fTablesEnergy) {
0204 for (auto ePair : aPair.second) {
0205 std::vector<G4double>& tableEnergy = fTablesEnergy[aPair.first][ePair.first];
0206 std::vector<G4double>& tableAlpha = fTablesAlpha[aPair.first][ePair.first];
0207 std::vector<G4double>& tableBeta = fTablesBeta[aPair.first][ePair.first];
0208
0209 std::vector<size_t> idx(tableEnergy.size());
0210 iota(idx.begin(), idx.end(), 0);
0211 std::sort(idx.begin(), idx.end(),
0212 [&tableEnergy](size_t i1, size_t i2) { return tableEnergy[i1] < tableEnergy[i2]; });
0213
0214 std::vector<std::vector<G4double>*> tables = {&tableEnergy, &tableAlpha, &tableBeta};
0215 for (std::vector<G4double>* table : tables) {
0216 std::vector<G4double> copy = *table;
0217 for (size_t i = 0; i < copy.size(); ++i) {
0218 (*table)[i] = copy[idx[i]];
0219 }
0220
0221
0222
0223 }
0224 }
0225 }
0226
0227 if (fVerboseLevel > 0) {
0228 G4cout << "RBE: read LEM data for the following cell lines and elements [number of points]:"
0229 << G4endl;
0230 for (auto aPair : fTablesEnergy) {
0231 G4cout << "- " << aPair.first << ": ";
0232 for (auto ePair : aPair.second) {
0233 G4cout << ePair.first << "[" << ePair.second.size() << "] ";
0234 }
0235 G4cout << G4endl;
0236 }
0237 }
0238 }
0239
0240
0241
0242 void RBE::SetCellLine(G4String name)
0243 {
0244 G4cout << "*************************" << G4endl << "*******SetCellLine*******" << G4endl
0245 << "*************************" << G4endl;
0246 if (fTablesEnergy.size() == 0) {
0247 G4Exception("RBE::SetCellLine", "NoCellLine", FatalException,
0248 "Cannot select cell line, probably LEM table not loaded yet?");
0249 }
0250 if (fTablesEnergy.find(name) == fTablesEnergy.end()) {
0251 std::stringstream str;
0252 str << "Cell line " << name << " not found in the LEM table.";
0253 G4Exception("RBE::SetCellLine", "", FatalException, str.str().c_str());
0254 }
0255 else {
0256 fAlphaX = fTablesAlphaX[name];
0257 fBetaX = fTablesBetaX[name];
0258 fDoseCut = fTablesDoseCut[name];
0259
0260 fActiveTableEnergy = &fTablesEnergy[name];
0261 fActiveTableAlpha = &fTablesAlpha[name];
0262 fActiveTableBeta = &fTablesBeta[name];
0263
0264 fMinZ = 0;
0265 fMaxZ = 0;
0266 fMinEnergies.clear();
0267 fMaxEnergies.clear();
0268
0269 for (auto energyPair : *fActiveTableEnergy) {
0270 if (!fMinZ || (energyPair.first < fMinZ)) fMinZ = energyPair.first;
0271 if (energyPair.first > fMaxZ) fMaxZ = energyPair.first;
0272
0273 fMinEnergies[energyPair.first] = energyPair.second[0];
0274 fMaxEnergies[energyPair.first] = energyPair.second[energyPair.second.size() - 1];
0275 }
0276 }
0277
0278 fActiveCellLine = name;
0279
0280 if (fVerboseLevel > 0) {
0281 G4cout << "RBE: cell line " << name << " selected." << G4endl;
0282 }
0283 }
0284
0285
0286
0287 std::tuple<G4double, G4double> RBE::GetHitAlphaAndBeta(G4double E, G4int Z)
0288 {
0289 if (!fActiveTableEnergy) {
0290 G4Exception("RBE::GetHitAlphaAndBeta", "NoCellLine", FatalException,
0291 "No cell line selected. Please, do it using the /rbe/cellLine command.");
0292 }
0293
0294
0295 if ((Z < fMinZ) || (Z > fMaxZ)) {
0296 if (fVerboseLevel > 2) {
0297 std::stringstream str;
0298 str << "Alpha & beta calculation supported only for ";
0299 str << fMinZ << " <= Z <= " << fMaxZ << ", but " << Z << " requested.";
0300 G4Exception("RBE::GetHitAlphaAndBeta", "", JustWarning, str.str().c_str());
0301 }
0302 return std::make_tuple<G4double, G4double>(0.0, 0.0);
0303 }
0304 if ((E < fMinEnergies[Z]) || (E >= fMaxEnergies[Z])) {
0305 if (fVerboseLevel > 2) {
0306 G4cout << "RBE hit: Z=" << Z << ", E=" << E << " => out of LEM table";
0307 if (fVerboseLevel > 3) {
0308 G4cout << " (" << fMinEnergies[Z] << " to " << fMaxEnergies[Z] << " MeV)";
0309 }
0310 G4cout << G4endl;
0311 }
0312 return std::make_tuple<G4double, G4double>(0.0, 0.0);
0313 }
0314
0315 std::vector<G4double>& vecEnergy = (*fActiveTableEnergy)[Z];
0316 std::vector<G4double>& vecAlpha = (*fActiveTableAlpha)[Z];
0317 std::vector<G4double>& vecBeta = (*fActiveTableBeta)[Z];
0318
0319
0320 const auto eLarger = upper_bound(begin(vecEnergy), end(vecEnergy), E);
0321 const G4int lower = distance(begin(vecEnergy), eLarger) - 1;
0322 const G4int upper = lower + 1;
0323
0324
0325 const G4double energyLower = vecEnergy[lower];
0326 const G4double energyUpper = vecEnergy[upper];
0327 const G4double energyFraction = (E - energyLower) / (energyUpper - energyLower);
0328
0329
0330 const G4double alpha =
0331 ((1 - energyFraction) * vecAlpha[lower] + energyFraction * vecAlpha[upper]);
0332 const G4double beta = ((1 - energyFraction) * vecBeta[lower] + energyFraction * vecBeta[upper]);
0333 if (fVerboseLevel > 2) {
0334 G4cout << "RBE hit: Z=" << Z << ", E=" << E << " => alpha=" << alpha << ", beta=" << beta
0335 << G4endl;
0336 }
0337
0338 return std::make_tuple(alpha, beta);
0339 }
0340
0341
0342
0343
0344 void RBE::ComputeAlphaAndBeta()
0345 {
0346
0347 if (!fCalculationEnabled) {
0348 if (fVerboseLevel > 0) {
0349 G4cout << "RBE::ComputeAlphaAndBeta() called but skipped as calculation not enabled"
0350 << G4endl;
0351 }
0352 return;
0353 }
0354
0355 if (fVerboseLevel > 0) {
0356 G4cout << "RBE: Computing alpha and beta..." << G4endl;
0357 }
0358
0359
0360 fAlpha.resize(fAlphaNumerator.size());
0361 fBeta.resize(fBetaNumerator.size());
0362
0363 for (size_t ii = 0; ii < fDenominator.size(); ii++) {
0364 if (fDenominator[ii] > 0) {
0365 fAlpha[ii] = fAlphaNumerator[ii] / (fDenominator[ii] * gray);
0366 fBeta[ii] = std::pow(fBetaNumerator[ii] / (fDenominator[ii] * gray), 2.0);
0367 }
0368
0369 else {
0370 fAlpha[ii] = 0.;
0371 fBeta[ii] = 0.;
0372 }
0373 }
0374 }
0375
0376
0377
0378 void RBE::ComputeRBE()
0379 {
0380
0381 if (!fCalculationEnabled) {
0382 if (fVerboseLevel > 0) {
0383 G4cout << "RBE::ComputeRBE() called but skipped as calculation not enabled" << G4endl;
0384 }
0385 return;
0386 }
0387
0388 if (fVerboseLevel > 0) {
0389 G4cout << "RBE: Computing survival and RBE..." << G4endl;
0390 }
0391 G4double smax = fAlphaX + 2 * fBetaX * fDoseCut;
0392
0393 for (G4int i = 0; i < VoxelizedSensitiveDetector::GetInstance()->GetTotalVoxelNumber(); i++) {
0394 if (std::isnan(fAlpha[i]) || std::isnan(fBeta[i])) {
0395 fLnS[i] = 0.0;
0396 fDoseX[i] = 0.0;
0397 }
0398 else if (fDose[i] <= fDoseCut) {
0399 fLnS[i] = -(fAlpha[i] * fDose[i]) - (fBeta[i] * (std::pow(fDose[i], 2.0)));
0400 fDoseX[i] = std::sqrt((-fLnS[i] / fBetaX) + std::pow((fAlphaX / (2 * fBetaX)), 2.0))
0401 - (fAlphaX / (2 * fBetaX));
0402 }
0403 else {
0404 G4double ln_Scut = -(fAlpha[i] * fDoseCut) - (fBeta[i] * (std::pow((fDoseCut), 2.0)));
0405 fLnS[i] = ln_Scut - ((fDose[i] - fDoseCut) * smax);
0406
0407 fDoseX[i] = ((-fLnS[i] + ln_Scut) / smax) + fDoseCut;
0408 }
0409 }
0410 fRBE = fDoseX / fDose;
0411 fSurvival = std::exp(fLnS);
0412 }
0413
0414
0415
0416 void RBE::Compute()
0417 {
0418
0419 if (!fCalculationEnabled) {
0420 if (fVerboseLevel > 0) {
0421 G4cout << "RBE::Compute() called but skipped as calculation not enabled" << G4endl;
0422 }
0423 return;
0424 }
0425
0426 if (fCalculated == true) return;
0427
0428 GetDose();
0429
0430 ComputeAlphaAndBeta();
0431 ComputeRBE();
0432
0433
0434 fCalculated = true;
0435 }
0436
0437
0438
0439 void RBE::GetDose()
0440 {
0441
0442 const Dose* dose = dynamic_cast<const Dose*>(Manager::GetInstance()->GetQuantity("Dose"));
0443 if (dose == nullptr)
0444 G4Exception("RBE::Compute", "RBEMissingDose", FatalException,
0445 "Trying to compute RBE without knowing the dose. Please add a valid dose or "
0446 "disable RBE calculation");
0447
0448
0449
0450 if (!dose->HasBeenCalculated())
0451 G4Exception("RBE::Compute", "RBEDoseNotCalculated", JustWarning,
0452 "Dose has not been calculated yet while computing RBE, that will be wrong."
0453 " Please, first calculate dose");
0454
0455 fDose = dose->GetDose();
0456 }
0457
0458
0459
0460 void RBE::SetDenominator(const RBE::array_type denom)
0461 {
0462 if (fVerboseLevel > 1) {
0463 G4cout << "RBE: Setting denominator..." << G4endl;
0464 }
0465 fDenominator = denom;
0466 }
0467
0468
0469
0470 void RBE::AddDenominator(const RBE::array_type denom)
0471 {
0472 if (fVerboseLevel > 1) {
0473 G4cout << "RBE: Adding denominator...";
0474 }
0475 if (fDenominator.size()) {
0476 fDenominator += denom;
0477 }
0478 else {
0479 if (fVerboseLevel > 1) {
0480 G4cout << " (created empty array)";
0481 }
0482 fDenominator = denom;
0483 }
0484 G4cout << G4endl;
0485 }
0486
0487
0488
0489 void RBE::SetAlphaNumerator(const RBE::array_type alpha)
0490 {
0491 if (fVerboseLevel > 1) {
0492 G4cout << "RBE: Setting alpha numerator..." << G4endl;
0493 }
0494 fAlphaNumerator = alpha;
0495 }
0496
0497
0498
0499 void RBE::SetBetaNumerator(const RBE::array_type beta)
0500 {
0501 if (fVerboseLevel > 1) {
0502 G4cout << "RBE: Setting beta numerator..." << G4endl;
0503 }
0504 fBetaNumerator = beta;
0505 }
0506
0507
0508
0509 void RBE::AddAlphaNumerator(const RBE::array_type alpha)
0510 {
0511 if (fVerboseLevel > 1) {
0512 G4cout << "RBE: Adding alpha numerator...";
0513 }
0514 if (fAlphaNumerator.size()) {
0515 fAlphaNumerator += alpha;
0516 }
0517 else {
0518 if (fVerboseLevel > 1) {
0519 G4cout << " (created empty array)";
0520 }
0521 fAlphaNumerator = alpha;
0522 }
0523 G4cout << G4endl;
0524 }
0525
0526
0527
0528 void RBE::AddBetaNumerator(const RBE::array_type beta)
0529 {
0530 if (fVerboseLevel > 1) {
0531 G4cout << "RBE: Adding beta numerator...";
0532 }
0533 if (fBetaNumerator.size()) {
0534 fBetaNumerator += beta;
0535 }
0536 else {
0537 if (fVerboseLevel > 1) {
0538 G4cout << " (created empty array)";
0539 }
0540 fBetaNumerator = beta;
0541 }
0542 G4cout << G4endl;
0543 }
0544
0545
0546
0547 void RBE::StoreAlphaAndBeta()
0548 {
0549
0550 if (!fCalculationEnabled) {
0551 if (fVerboseLevel > 0) {
0552 G4cout << "RBE::StoreAlphaAndBeta() called but skipped as calculation not enabled" << G4endl;
0553 }
0554 return;
0555 }
0556
0557 G4String AlphaBetaPath = fPath + "_AlphaAndBeta.out";
0558 if (fVerboseLevel > 1) {
0559 G4cout << "RBE: Writing alpha and beta..." << G4endl;
0560 }
0561 std::ofstream ofs(AlphaBetaPath);
0562
0563 Compute();
0564
0565 if (ofs.is_open()) {
0566 ofs << std::left << std::setw(width) << "i" << std::setw(width) << "j" << std::setw(width)
0567 << "k" << std::setw(width) << "alpha" << std::setw(width) << "beta " << G4endl;
0568
0569 auto voxSensDet = VoxelizedSensitiveDetector::GetInstance();
0570
0571
0572
0573 for (G4int i = 0; i < voxSensDet->GetVoxelNumberAlongX(); i++)
0574 for (G4int j = 0; j < voxSensDet->GetVoxelNumberAlongY(); j++)
0575 for (G4int k = 0; k < voxSensDet->GetVoxelNumberAlongZ(); k++) {
0576 G4int v = voxSensDet->GetThisVoxelNumber(i, j, k);
0577
0578 ofs << std::left << std::setw(width) << i << std::setw(width) << j << std::setw(width)
0579 << k << std::setw(width) << (std::isnan(fAlpha[v]) ? 0 : (fAlpha[v] * gray))
0580 << std::setw(width) << (std::isnan(fBeta[v]) ? 0 : (fBeta[v] * std::pow(gray, 2.0)))
0581 << G4endl;
0582 }
0583 }
0584 if (fVerboseLevel > 0) {
0585 G4cout << "RBE: Alpha and beta written to " << AlphaBetaPath << G4endl;
0586 }
0587 }
0588
0589
0590
0591 void RBE::StoreRBE()
0592 {
0593
0594 if (!fCalculationEnabled) {
0595 if (fVerboseLevel > 0) {
0596 G4cout << "RBE::StoreRBE() called but skipped as calculation not enabled" << G4endl;
0597 }
0598 return;
0599 }
0600
0601 G4String RBEPath = fPath + "_RBE.out";
0602 if (fSaved == true)
0603 G4Exception("RBE::StoreRBE", "RBEOverwrite", JustWarning,
0604 "Overwriting RBE file. For multiple runs, change filename.");
0605 std::ofstream ofs(RBEPath);
0606
0607 Compute();
0608
0609 if (ofs.is_open()) {
0610 ofs << std::left << std::setw(width) << "i" << std::setw(width) << "j" << std::setw(width)
0611 << "k" << std::setw(width) << "Dose(Gy)" << std::setw(width) << "ln(S) " << std::setw(width)
0612 << "Survival" << std::setw(width) << "DoseB(Gy)" << std::setw(width) << "RBE" << G4endl;
0613
0614 auto voxSensDet = VoxelizedSensitiveDetector::GetInstance();
0615
0616 for (G4int i = 0; i < voxSensDet->GetVoxelNumberAlongX(); i++)
0617 for (G4int j = 0; j < voxSensDet->GetVoxelNumberAlongY(); j++)
0618 for (G4int k = 0; k < voxSensDet->GetVoxelNumberAlongZ(); k++) {
0619 G4int v = voxSensDet->GetThisVoxelNumber(i, j, k);
0620
0621 ofs << std::left << std::setw(width) << i << std::setw(width) << j << std::setw(width)
0622 << k << std::setw(width) << (fDose[v] / gray) << std::setw(width) << fLnS[v]
0623 << std::setw(width) << fSurvival[v] << std::setw(width) << fDoseX[v] / gray
0624 << std::setw(width) << (std::isnan(fRBE[v]) ? 0. : fRBE[v]) << G4endl;
0625 }
0626 }
0627 if (fVerboseLevel > 0) {
0628 G4cout << "RBE: RBE written to " << RBEPath << G4endl;
0629 }
0630
0631 fSaved = true;
0632 }
0633
0634
0635
0636 void RBE::Reset()
0637 {
0638 if (fVerboseLevel > 1) {
0639 G4cout << "RBE: Reset(): ";
0640 }
0641 fAlphaNumerator = 0.0;
0642 fBetaNumerator = 0.0;
0643 fDenominator = 0.0;
0644 fDose = 0.0;
0645 fCalculated = false;
0646 if (fVerboseLevel > 1) {
0647 G4cout << fAlphaNumerator.size() << " points." << G4endl;
0648 }
0649 }
0650
0651
0652
0653 void RBE::AddFromAccumulable(G4VAccumulable* GenAcc)
0654 {
0655 RBEAccumulable* acc = (RBEAccumulable*)GenAcc;
0656 AddAlphaNumerator(acc->GetAlphaNumerator());
0657 AddBetaNumerator(acc->GetBetaNumerator());
0658 AddDenominator(acc->GetDenominator());
0659
0660 fCalculated = false;
0661 }
0662
0663
0664
0665 }