File indexing completed on 2025-02-23 09:22:01
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
0031
0032
0033
0034
0035 #include "DNAGeometry.hh"
0036
0037 #include "DNAGeometryMessenger.hh"
0038 #include "OctreeNode.hh"
0039 #include "PlacementVolumeInfo.hh"
0040 #include "VirtualChromosome.hh"
0041
0042 #include "G4Box.hh"
0043 #include "G4Color.hh"
0044 #include "G4Ellipsoid.hh"
0045 #include "G4NistManager.hh"
0046 #include "G4PVParameterised.hh"
0047 #include "G4PVPlacement.hh"
0048 #include "G4PhysicalConstants.hh"
0049 #include "G4VisAttributes.hh"
0050
0051 #include <cmath>
0052 #include <fstream>
0053
0054
0055
0056 G4bool compareLVByName::operator()(const G4LogicalVolume* lhs, const G4LogicalVolume* rhs) const
0057 {
0058 return lhs->GetName() < rhs->GetName();
0059 }
0060
0061
0062
0063 DNAGeometry::DNAGeometry()
0064 {
0065 fpMessenger = new DNAGeometryMessenger(this);
0066 fpChromosomeMapper = new ChromosomeMapper();
0067
0068
0069 fpDamageModel = new DamageModel();
0070 G4NistManager* man = G4NistManager::Instance();
0071 fpSugarMaterial = man->FindOrBuildMaterial("G4_DNA_DEOXYRIBOSE");
0072 fpMediumMaterial = man->FindOrBuildMaterial("G4_WATER");
0073 fpPhosphateMaterial = man->FindOrBuildMaterial("G4_DNA_PHOSPHATE");
0074 fpGuanineMaterial = man->FindOrBuildMaterial("G4_DNA_GUANINE");
0075 fpAdenineMaterial = man->FindOrBuildMaterial("G4_DNA_ADENINE");
0076 fpThymineMaterial = man->FindOrBuildMaterial("G4_DNA_THYMINE");
0077 fpCytosineMaterial = man->FindOrBuildMaterial("G4_DNA_CYTOSINE");
0078 fpHistoneMaterial = man->FindOrBuildMaterial("G4_WATER");
0079 fpDNAPhysicsWorld = new DNAWorld();
0080 fpDrawCellAttrs = new G4VisAttributes();
0081 fpDrawCellAttrs->SetColor(G4Color(0, 0, 1, 0.2));
0082 fpDrawCellAttrs->SetForceSolid(true);
0083
0084 fMoleculeSizes["phosphate"] = G4ThreeVector(2.282354, 2.282354, 2.282354) * angstrom;
0085 fMoleculeSizes["sugar"] = G4ThreeVector(2.632140, 2.632140, 2.632140) * angstrom;
0086 fMoleculeSizes["guanine"] = G4ThreeVector(3.631503, 3.799953, 1.887288) * angstrom;
0087 fMoleculeSizes["cytosine"] = G4ThreeVector(3.597341, 3.066331, 1.779361) * angstrom;
0088 fMoleculeSizes["adenine"] = G4ThreeVector(3.430711, 3.743504, 1.931958) * angstrom;
0089 fMoleculeSizes["thymine"] = G4ThreeVector(4.205943, 3.040448, 2.003359) * angstrom;
0090 fMoleculeSizes["histone"] = G4ThreeVector(25, 25, 25) * angstrom;
0091 }
0092
0093
0094
0095 DNAGeometry::~DNAGeometry()
0096 {
0097 delete fpMessenger;
0098 }
0099
0100
0101
0102 void DNAGeometry::BuildDNA(G4LogicalVolume* vol)
0103 {
0104
0105 fpDNAVolumeChem = vol;
0106
0107
0108 auto* volAsEllipsoid = dynamic_cast<G4Ellipsoid*>(vol->GetSolid());
0109 auto* volAsBox = dynamic_cast<G4Box*>(vol->GetSolid());
0110
0111 G4VSolid* cloneSolid;
0112 if (volAsEllipsoid != nullptr) {
0113 cloneSolid = new G4Ellipsoid(*((G4Ellipsoid*)vol->GetSolid()));
0114 }
0115 else if (volAsBox != nullptr) {
0116 cloneSolid = new G4Box(*((G4Box*)vol->GetSolid()));
0117 }
0118 else {
0119 G4ExceptionDescription errmsg;
0120 errmsg << "An invalid physical volume shape has been used to hold the DNA volume" << G4endl;
0121 G4Exception("MolecularDnaGeometry", "ERR_INVALID_DNA_SHAPE", FatalException, errmsg);
0122 return;
0123 }
0124
0125 fpDNAVolumePhys = new G4LogicalVolume(cloneSolid, vol->GetMaterial(),
0126 "DNAPhysLV");
0127 FillParameterisedSpace();
0128 fpDNAPhysicsWorld->SetDNAVolumePointer(fpDNAVolumePhys);
0129 }
0130
0131
0132
0133 void DNAGeometry::FillParameterisedSpace()
0134 {
0135
0136
0137 std::vector<placement> placementVector;
0138 std::map<G4LogicalVolume*, G4int> currentCopyNumber;
0139 G4int numberOfChains = 0;
0140
0141
0142 std::map<G4String, G4LogicalVolume*> namesToLVs;
0143 for (const auto& it : fVoxelNames) {
0144 G4String thisVoxelName = it.first;
0145 G4String thisVoxelFile = it.second;
0146
0147 auto voxel = LoadVoxelVolume(thisVoxelName, thisVoxelFile);
0148 G4LogicalVolume* lv = voxel.first;
0149 fInfoMap[lv] = voxel.second;
0150 namesToLVs[thisVoxelName] = lv;
0151 currentCopyNumber[lv] = 0;
0152 numberOfChains = this->GetPVInfo(lv)->GetNumberOfChains();
0153 if (!(numberOfChains == 1 || numberOfChains == 4 || numberOfChains == 8)) {
0154 G4cout << "Geometry contains volumes with none of 1/4/8"
0155 << " chains. This can cause errors" << G4endl;
0156 }
0157 }
0158
0159
0160 for (unsigned ii = 0; ii < fVoxelTypes.size(); ii++) {
0161 G4LogicalVolume* lv = namesToLVs[fVoxelTypes[ii]];
0162
0163 placement thisPlacement = std::make_tuple(lv, fVoxelIndices[ii], currentCopyNumber[lv]++,
0164 fVoxelPositions[ii], fVoxelRotations[ii]);
0165 placementVector.push_back(thisPlacement);
0166 }
0167
0168
0169 std::map<G4String, int64_t> histonesPerChromosome;
0170 std::map<G4String, int64_t> basePairsPerChromosome;
0171
0172 for (auto it = placementVector.begin(); it != placementVector.end(); it++) {
0173 G4LogicalVolume* thisLogical = std::get<0>(*it);
0174 if (thisLogical == nullptr) {
0175 G4Exception("DNAGeometry::FillParameterisedSpace", "ERR_LOG_VOL_EXISTS_FALSE", FatalException,
0176 "At least one of the placement names"
0177 " specified doesn't exist");
0178 }
0179 G4int thisIndex = std::get<1>(*it);
0180 G4int thisCopyNo = std::get<2>(*it);
0181
0182 G4ThreeVector thisPosition = std::get<3>(*it);
0183 G4ThreeVector thisRotation = std::get<4>(*it);
0184 std::stringstream ss;
0185 ss << thisLogical->GetName() << "-" << thisIndex;
0186
0187 auto inv = new G4RotationMatrix();
0188 inv->rotateX(thisRotation.getX());
0189 inv->rotateY(thisRotation.getY());
0190 inv->rotateZ(thisRotation.getZ());
0191 auto pRot = new G4RotationMatrix(inv->inverse());
0192 delete inv;
0193
0194 G4bool isPhysicallyPlaced = false;
0195 VirtualChromosome* thisChromosome = this->GetChromosomeMapper()->GetChromosome(thisPosition);
0196 if (thisChromosome != nullptr) {
0197 isPhysicallyPlaced = true;
0198
0199 new G4PVPlacement(pRot, thisPosition, thisLogical, ss.str(), fpDNAVolumePhys, false,
0200 thisCopyNo, this->GetOverlaps());
0201
0202 new G4PVPlacement(pRot, thisPosition, this->GetMatchingChemVolume(thisLogical), ss.str(),
0203 fpDNAVolumeChem, false, thisCopyNo, this->GetOverlaps());
0204
0205
0206 if (histonesPerChromosome.find(thisChromosome->GetName()) == histonesPerChromosome.end()) {
0207 histonesPerChromosome[thisChromosome->GetName()] = 0;
0208 }
0209 histonesPerChromosome[thisChromosome->GetName()] +=
0210 this->GetPVInfo(thisLogical)->GetTotalHistones();
0211
0212
0213 if (basePairsPerChromosome.find(thisChromosome->GetName()) == basePairsPerChromosome.end()) {
0214 basePairsPerChromosome[thisChromosome->GetName()] = 0;
0215 }
0216 basePairsPerChromosome[thisChromosome->GetName()] +=
0217 this->GetPVInfo(thisLogical)->GetTotalBasePairs();
0218 }
0219
0220
0221
0222 if (numberOfChains == 4 || numberOfChains == 8) {
0223 AddFourChainPlacement(it, placementVector.begin(), isPhysicallyPlaced);
0224 }
0225 else if (numberOfChains == 1) {
0226 AddSingleChainPlacement(it, placementVector.begin(), isPhysicallyPlaced);
0227 }
0228 else {
0229 G4ExceptionDescription errmsg;
0230 errmsg << "Having none of 1/4/8 chains in a placement is not "
0231 << "supported, the application will crash" << G4endl;
0232 G4Exception("DNAGeometry::FillParameterisedSpace", "Number of chains not supported",
0233 FatalException, errmsg);
0234 }
0235 }
0236
0237
0238 G4cout << "Histones placed per chromosome:" << G4endl;
0239 G4cout << "Chromosome Histones" << G4endl;
0240 G4cout << "-----------------------------------" << G4endl;
0241 for (auto& it : histonesPerChromosome) {
0242 G4cout << it.first.c_str() << " : " << it.second << G4endl;
0243 }
0244
0245
0246 G4cout << "Base Pairs placed per chromosome:" << G4endl;
0247 G4cout << "Chromosome Base Pairs" << G4endl;
0248 G4cout << "-----------------------------------" << G4endl;
0249 for (auto& it : basePairsPerChromosome) {
0250 G4cout << it.first.c_str() << " : " << it.second << G4endl;
0251 }
0252 G4cout << "-------------------------------" << G4endl;
0253
0254 if (this->GetVerbosity() > 0) {
0255 this->PrintOctreeStats();
0256 }
0257 }
0258
0259
0260
0261 void DNAGeometry::AddSingleChainPlacement(const std::vector<placement>::iterator it,
0262 const std::vector<placement>::iterator,
0263 G4bool isPhysicallyPlaced)
0264 {
0265 G4LogicalVolume* thisLogical = std::get<0>(*it);
0266 G4bool twist = fVoxelTwist[thisLogical->GetName()];
0267 AddNewPlacement(thisLogical, {{0, 1, 2, 3, 4, 5, 6, 7}}, twist, isPhysicallyPlaced);
0268 }
0269
0270
0271
0272 void DNAGeometry::AddFourChainPlacement(const std::vector<placement>::iterator it,
0273 const std::vector<placement>::iterator begin,
0274 G4bool isPhysicallyPlaced)
0275 {
0276 G4LogicalVolume* thisLogical = std::get<0>(*it);
0277 G4int thisIndex = std::get<1>(*it);
0278
0279 std::array<int, 8> global_chain{};
0280 G4bool twist = fVoxelTwist[thisLogical->GetName()];
0281 if (it == begin) {
0282 global_chain = {{0, 1, 2, 3, 4, 5, 6, 7}};
0283 }
0284 else {
0285
0286 placement previous = *(it - 1);
0287 G4ThreeVector oldRotation = std::get<4>(previous);
0288 G4ThreeVector thisRotation = std::get<4>(*it);
0289
0290 G4RotationMatrix rotnew;
0291 rotnew.rotateX(thisRotation.getX());
0292 rotnew.rotateY(thisRotation.getY());
0293 rotnew.rotateZ(thisRotation.getZ());
0294
0295 rotnew.rectify();
0296
0297 G4RotationMatrix rotold;
0298 rotold.rotateX(oldRotation.getX());
0299 rotold.rotateY(oldRotation.getY());
0300 rotold.rotateZ(oldRotation.getZ());
0301
0302 G4ThreeVector zdiff = rotold.colZ() - rotnew.colZ();
0303 if (zdiff.mag2() > 0.1) {
0304 rotold = rotold.rotate(halfpi, rotold.colY());
0305 }
0306 rotold.rectify();
0307
0308
0309
0310 G4RotationMatrix relative = rotnew * rotold.inverse();
0311 relative.rectify();
0312 G4ThreeVector axis = relative.getAxis();
0313 G4double angle = relative.getDelta();
0314
0315 G4int sign = (axis.dot(rotnew.colZ()) >= 0) ? 1 : -1;
0316 if (sign < 0) {
0317 angle = 2 * pi - angle;
0318 }
0319 G4int zrots = ((G4int)(2.05 * angle / pi)) % 4;
0320
0321 global_chain = {{(this->GetGlobalChain(thisIndex - 1, 0) + zrots) % 4,
0322 (this->GetGlobalChain(thisIndex - 1, 1) + zrots) % 4,
0323 (this->GetGlobalChain(thisIndex - 1, 2) + zrots) % 4,
0324 (this->GetGlobalChain(thisIndex - 1, 3) + zrots) % 4,
0325 4 + (this->GetGlobalChain(thisIndex - 1, 4) + zrots) % 4,
0326 4 + (this->GetGlobalChain(thisIndex - 1, 5) + zrots) % 4,
0327 4 + (this->GetGlobalChain(thisIndex - 1, 6) + zrots) % 4,
0328 4 + (this->GetGlobalChain(thisIndex - 1, 7) + zrots) % 4}};
0329 twist = false;
0330 }
0331 this->AddNewPlacement(thisLogical, global_chain, twist, isPhysicallyPlaced);
0332 }
0333
0334
0335
0336 void DNAGeometry::AddNewPlacement(const G4LogicalVolume* lv, std::array<int, 8> global_chain,
0337 G4bool twist, G4bool isPhysicallyPlaced)
0338 {
0339 placement_transform pt;
0340 std::array<int64_t, 8> start{};
0341 std::array<int64_t, 8> end{};
0342
0343
0344 if (fPlacementTransformations.empty()) {
0345 start = {{0, 0, 0, 0, 0, 0, 0, 0}};
0346 end = {{this->GetPVInfo(lv)->GetPairsOnChain(global_chain[0]),
0347 this->GetPVInfo(lv)->GetPairsOnChain(global_chain[1]),
0348 this->GetPVInfo(lv)->GetPairsOnChain(global_chain[2]),
0349 this->GetPVInfo(lv)->GetPairsOnChain(global_chain[3])}};
0350 }
0351 else {
0352 placement_transform previous = fPlacementTransformations.back();
0353 std::array<int64_t, 8> previousEnd = std::get<2>(previous);
0354
0355 start = {{previousEnd[0], previousEnd[1], previousEnd[2], previousEnd[3], previousEnd[4],
0356 previousEnd[5], previousEnd[6], previousEnd[7]}};
0357 end = {{previousEnd[0] + this->GetPVInfo(lv)->GetPairsOnChain(global_chain[0]),
0358 previousEnd[1] + this->GetPVInfo(lv)->GetPairsOnChain(global_chain[1]),
0359 previousEnd[2] + this->GetPVInfo(lv)->GetPairsOnChain(global_chain[2]),
0360 previousEnd[3] + this->GetPVInfo(lv)->GetPairsOnChain(global_chain[3]),
0361 previousEnd[4] + this->GetPVInfo(lv)->GetPairsOnChain(global_chain[4]),
0362 previousEnd[5] + this->GetPVInfo(lv)->GetPairsOnChain(global_chain[5]),
0363 previousEnd[6] + this->GetPVInfo(lv)->GetPairsOnChain(global_chain[6]),
0364 previousEnd[7] + this->GetPVInfo(lv)->GetPairsOnChain(global_chain[7])}};
0365 }
0366 pt = std::make_tuple(global_chain, start, end, twist, isPhysicallyPlaced);
0367 fPlacementTransformations.push_back(pt);
0368 }
0369
0370
0371 std::pair<G4LogicalVolume*, PlacementVolumeInfo*>
0372 DNAGeometry::LoadVoxelVolume(const G4String& volumeName, const G4String& filename)
0373 {
0374 if (this->GetVerbosity() > 0) {
0375 G4cout << "Loading Voxel: " << volumeName << G4endl;
0376 }
0377
0378 auto thisInfo = new PlacementVolumeInfo();
0379
0380 G4double vxdim = 0.5 * fVoxelSideLength.getX();
0381 G4double vydim = 0.5 * fVoxelSideLength.getY();
0382 G4double vzdim = 0.5 * fVoxelSideLength.getZ();
0383
0384
0385 auto physicsPhysical = new G4Box(volumeName, vxdim, vydim, vzdim);
0386 auto physicsLogical = new G4LogicalVolume(physicsPhysical, fpMediumMaterial, volumeName);
0387 auto chemPhysical = new G4Box(volumeName, vxdim, vydim, vzdim);
0388 auto chemLogical = new G4LogicalVolume(chemPhysical, fpMediumMaterial, volumeName);
0389
0390 if (this->GetDrawCellVolumes()) {
0391 chemLogical->SetVisAttributes(fpDrawCellAttrs);
0392 }
0393
0394 fChemToPhys[chemLogical] = physicsLogical;
0395 fPhysToChem[physicsLogical] = chemLogical;
0396
0397 physicsLogical->SetSmartless(this->GetSmartless());
0398
0399 auto thisOctree = new OctreeNode(G4ThreeVector(0, 0, 0), G4ThreeVector(vxdim, vydim, vzdim), 1);
0400 auto thisHistoneOctree =
0401 new OctreeNode(G4ThreeVector(0, 0, 0), G4ThreeVector(vxdim, vydim, vzdim), 1);
0402
0403
0404 if (!utility::Path_exists(filename)) {
0405 G4ExceptionDescription errmsg;
0406 errmsg << "The file: " << filename << " could not be found." << G4endl;
0407 G4Exception("DNAGeometry::LoadVoxelVolume", "File Not Found", FatalException, errmsg);
0408 }
0409 std::ifstream infile(filename, std::ifstream::in);
0410 G4String currentline;
0411 G4String lower_name;
0412 G4ThreeVector pos;
0413 G4ThreeVector rot;
0414 G4ThreeVector mol_size;
0415 std::vector<molecule_t> molecules;
0416 std::vector<molecule_t> histones;
0417 G4bool file_contains_size = false;
0418 G4int line_fields = 0;
0419 G4int line_number = 0;
0420 G4int uncommented_line_number = 0;
0421
0422 if (infile.is_open()) {
0423 while (getline(infile, currentline)) {
0424 if (currentline[0] != '#') {
0425 std::vector<G4String> line = utility::Split(currentline, ' ');
0426
0427 if (uncommented_line_number == 0) {
0428 line_fields = line.size();
0429 file_contains_size = (line_fields == 14);
0430 }
0431 if ((line_fields != 10) && (line_fields != 14)) {
0432 G4ExceptionDescription errmsg;
0433 errmsg << filename << " should have 10 or 14 fields, only got " << line_fields << G4endl;
0434 G4Exception("DNAGeometry::LoadVoxelVolume", "BadFileFormat", FatalErrorInArgument,
0435 errmsg);
0436 }
0437 if (line_fields != (G4int)line.size()) {
0438 G4ExceptionDescription errmsg;
0439 errmsg << "Unexpected number of fields on line " << line_number << " of file " << filename
0440 << G4endl << "Expected " << line_fields << " fields, but got " << line.size()
0441 << G4endl;
0442 G4Exception("DNAGeometry::LoadVoxelVolume", "BadFileFormat", FatalErrorInArgument,
0443 errmsg);
0444 }
0445 molecule_t thisMolecule;
0446
0447 thisMolecule.fname = (G4String)line.at(0);
0448 thisMolecule.fshape = (G4String)line.at(1);
0449 thisMolecule.fchain_id = (G4int)std::stoi(line.at(2));
0450 thisMolecule.fstrand_id = (G4int)std::stoi(line.at(3));
0451 thisMolecule.fbase_idx = (int64_t)std::stoll(line.at(4));
0452
0453 lower_name = (G4String)line.at(0);
0454 G4StrUtil::to_lower(lower_name);
0455
0456
0457 G4int size_offset = (file_contains_size) ? 3 : 0;
0458 if (file_contains_size) {
0459 mol_size =
0460 G4ThreeVector(std::stod(line.at(5)) * angstrom, std::stod(line.at(6)) * angstrom,
0461 std::stod(line.at(7)) * angstrom);
0462 }
0463 else {
0464 if (fMoleculeSizes.count(lower_name) == 0) {
0465 G4ExceptionDescription errmsg;
0466 errmsg << "A molecule size has not been defined for " << lower_name << G4endl;
0467 G4Exception("DNAGeometry::LoadVoxelVolume", "MoleculeNotDefined", FatalErrorInArgument,
0468 errmsg);
0469 }
0470 mol_size = fMoleculeSizes.at(lower_name);
0471 }
0472 pos = G4ThreeVector(std::stod(line.at(5 + size_offset)) * angstrom,
0473 std::stod(line.at(6 + size_offset)) * angstrom,
0474 std::stod(line.at(7 + size_offset)) * angstrom);
0475
0476 rot =
0477 G4ThreeVector(std::stod(line.at(8 + size_offset)), std::stod(line.at(9 + size_offset)),
0478 std::stod(line.at(10 + size_offset)));
0479
0480 thisMolecule.fsize = mol_size;
0481 thisMolecule.fposition = pos;
0482 thisMolecule.frotation = rot;
0483
0484 if (lower_name == "histone") {
0485 histones.push_back(thisMolecule);
0486 }
0487 else {
0488 molecules.push_back(thisMolecule);
0489 }
0490 uncommented_line_number++;
0491 }
0492 line_number++;
0493 }
0494 infile.close();
0495 }
0496 else {
0497 G4ExceptionDescription errmsg;
0498 errmsg << "Voxel Position file read error" << G4endl << "Name: " << volumeName << G4endl
0499 << "Filename: " << filename << G4endl << "Format should be: "
0500 << "# NAME SHAPE CHAIN_ID STRAND_ID BP_INDEX SIZE_X SIZE_Y "
0501 << "SIZE_Z POS_X POS_Y POS_Z ROT_X ROT_Y ROT_Z" << G4endl << "or" << G4endl
0502 << "# NAME SHAPE CHAIN_ID STRAND_ID BP_INDEX POS_X POS_Y POS_Z "
0503 "ROT_X ROT_Y ROT_Z"
0504 << G4endl
0505 << "Separator is space, lines with '#' as the first character are "
0506 "commented"
0507 << G4endl;
0508
0509 G4Exception("DNAGeometry::LoadVoxelVolume", "BadFile", FatalErrorInArgument, errmsg);
0510 }
0511
0512
0513
0514 G4VPhysicalVolume* pv_placement = nullptr;
0515
0516
0517 G4int his_arr_size = histones.size();
0518 for (G4int ii = 0; ii != his_arr_size; ++ii) {
0519 molecule_t thisMolecule = histones[ii];
0520 if (this->GetVerbosity() > 4) {
0521 G4cout << "Placing molecule " << ii << ": " << thisMolecule.fname << G4endl;
0522 }
0523
0524 pv_placement = PlaceHistone(physicsLogical, thisMolecule);
0525 thisHistoneOctree->AddPhysicalVolume(pv_placement);
0526 }
0527 thisInfo->SetNHistones(his_arr_size);
0528 thisInfo->SetHistoneOctree(thisHistoneOctree);
0529
0530
0531 G4int mol_arr_size = molecules.size();
0532 for (G4int ii = 0; ii != mol_arr_size; ii++) {
0533 molecule_t thisMolecule = molecules[ii];
0534 if (this->GetVerbosity() > 4) {
0535 G4cout << "Placing molecule " << ii << ": " << thisMolecule.fname << G4endl;
0536 }
0537
0538 if (thisInfo->GetPairsOnChain(thisMolecule.fchain_id) < (thisMolecule.fbase_idx + 1)) {
0539 thisInfo->SetPairsOnChain(thisMolecule.fchain_id, thisMolecule.fbase_idx + 1);
0540 }
0541
0542
0543 if (thisMolecule.fname == "Sugar") {
0544 molecule_t phosphate = molecules[ii - 1];
0545
0546 G4int phosph_idx = ii - 1;
0547 if ((phosph_idx >= 0) && (phosph_idx < (G4int)mol_arr_size)) {
0548 phosphate = molecules[phosph_idx];
0549 if (phosphate.fchain_id != thisMolecule.fchain_id) {
0550
0551 phosphate = molecule_t();
0552 phosphate.fname = "wall";
0553 }
0554 }
0555 else {
0556
0557 phosphate = molecule_t();
0558 phosphate.fname = "wall";
0559 }
0560 pv_placement = PlaceSugar(physicsLogical, thisMolecule, phosphate);
0561 }
0562 else if (thisMolecule.fname == "Phosphate") {
0563 molecule_t sugar;
0564 G4int sugar_idx = (thisMolecule.fstrand_id == 0) ? ii + 7 : ii - 5;
0565 if ((sugar_idx >= 0) && (sugar_idx < mol_arr_size)) {
0566 sugar = molecules[sugar_idx];
0567 if (sugar.fchain_id != thisMolecule.fchain_id) {
0568
0569 sugar = molecule_t();
0570 sugar.fname = "wall";
0571 }
0572 }
0573 else {
0574
0575 sugar = molecule_t();
0576 sugar.fname = "wall";
0577 }
0578 pv_placement = PlacePhosphate(physicsLogical, thisMolecule, sugar);
0579 }
0580 else if ((thisMolecule.fname == "Guanine") || (thisMolecule.fname == "Adenine")
0581 || (thisMolecule.fname == "Cytosine") || (thisMolecule.fname == "Thymine"))
0582 {
0583 molecule_t sugar = molecules[ii - 1];
0584 molecule_t oppBasePair;
0585 molecule_t nextBasePair;
0586
0587 G4int nextBPidx;
0588 G4bool endOfChain = false;
0589 G4bool nextBPexists;
0590 if (thisMolecule.fstrand_id == 0) {
0591 oppBasePair = molecules[ii + 3];
0592 nextBPexists = (ii + 6 < mol_arr_size);
0593 if (nextBPexists) {
0594 endOfChain = (thisMolecule.fchain_id != molecules[ii + 6].fchain_id);
0595 }
0596 nextBPidx = (nextBPexists && !endOfChain) ? ii + 6 : ii - 6;
0597 }
0598 else {
0599 oppBasePair = molecules[ii - 3];
0600 nextBPexists = (ii - 6 >= 0);
0601 if (nextBPexists) {
0602 endOfChain = (thisMolecule.fchain_id != molecules[ii - 6].fchain_id);
0603 }
0604 nextBPidx = (nextBPexists && !endOfChain) ? ii - 6 : ii + 6;
0605 }
0606 nextBasePair = molecules[nextBPidx];
0607 pv_placement = PlaceBase(physicsLogical, thisMolecule, oppBasePair, nextBasePair, sugar);
0608 }
0609 else {
0610 G4ExceptionDescription errmsg;
0611 errmsg << "Molecule name: " << thisMolecule.fname << " is invalid." << G4endl << "Aborting"
0612 << G4endl;
0613 G4Exception("DNAGeometry::LoadVoxelVolume", "Invalid molecule name", FatalException, errmsg);
0614 }
0615 if (this->GetOverlaps()) {
0616 if (pv_placement->CheckOverlaps()) {
0617
0618
0619 G4cout << "Overlap, translation: " << pv_placement->GetFrameTranslation() << G4endl;
0620 G4cout << "Overlap, rotationX: " << pv_placement->GetFrameRotation()->colX() << G4endl;
0621 G4cout << "Overlap, rotationY: " << pv_placement->GetFrameRotation()->colY() << G4endl;
0622 G4cout << "Overlap, rotationZ: " << pv_placement->GetFrameRotation()->colZ() << G4endl;
0623 }
0624 }
0625 if (this->GetDrawCellVolumes()) {
0626
0627 pv_placement->GetLogicalVolume()->SetVisAttributes(G4VisAttributes::GetInvisible());
0628 }
0629 thisOctree->AddPhysicalVolume(pv_placement);
0630 }
0631 thisInfo->SetOctree(thisOctree);
0632 return std::make_pair(physicsLogical, thisInfo);
0633 }
0634
0635 void DNAGeometry::FillVoxelVectors()
0636 {
0637 G4String filename = fFractalCurveFile;
0638 if (!utility::Path_exists(filename)) {
0639 G4ExceptionDescription errmsg;
0640 errmsg << "The file: " << filename << " could not be found." << G4endl;
0641 G4Exception("DNAGeometry::FillVoxelVectors", "File Not Found", FatalException, errmsg);
0642 }
0643 std::ifstream infile(filename, std::ifstream::in);
0644 G4String currentline;
0645 G4ThreeVector pos;
0646 G4ThreeVector rot;
0647
0648 fVoxelTypes.clear();
0649 fVoxelIndices.clear();
0650 fVoxelPositions.clear();
0651 fVoxelRotations.clear();
0652
0653 if (infile.is_open()) {
0654 while (getline(infile, currentline)) {
0655 if (currentline[0] != '#') {
0656 try {
0657 G4int pi_or_one = fAnglesAsPi ? pi : 1;
0658 std::vector<G4String> line = utility::Split(currentline, ' ');
0659 fVoxelIndices.push_back(std::stoi(line.at(0)));
0660 fVoxelTypes.push_back((G4String)line.at(1));
0661 pos = G4ThreeVector(std::stod(line.at(2)) * fFractalScaling.getX(),
0662 std::stod(line.at(3)) * fFractalScaling.getY(),
0663 std::stod(line.at(4)) * fFractalScaling.getZ());
0664 rot = G4ThreeVector(std::stod(line.at(5)), std::stod(line.at(6)), std::stod(line.at(7)));
0665 fVoxelPositions.push_back(pos);
0666 fVoxelRotations.push_back(rot * pi_or_one);
0667 }
0668 catch (int exception) {
0669 G4cout << "Voxel Position file read error" << G4endl;
0670 G4cout << "Got Line: " << currentline << G4endl;
0671 G4cout << "Format should be: "
0672 << "IDX KIND POS_X POS_Y POS_Z EUL_PSI EUL_THETA"
0673 << " EUL_PHI" << G4endl;
0674 G4cout << "Separator is space, lines with '#' as the first "
0675 << "character are commented" << G4endl;
0676 std::exit(1);
0677 }
0678 }
0679 }
0680 infile.close();
0681 }
0682 else {
0683 G4cout << "Voxel Position file read error on open" << G4endl;
0684 G4cout << "Format should be: "
0685 << "IDX KIND POS_X POS_Y POS_Z EUL_PSI EUL_THETA EUL_PHI" << G4endl;
0686 G4cout << "Separator is space, lines with '#' as the first "
0687 << "character are commented" << G4endl;
0688 }
0689 }
0690
0691 OctreeNode* DNAGeometry::GetTopOctreeNode(G4LogicalVolume* lv) const
0692 {
0693 const PlacementVolumeInfo* info = GetPVInfo(lv);
0694 return (info == nullptr) ? nullptr : info->GetOctree();
0695 }
0696
0697 const PlacementVolumeInfo* DNAGeometry::GetPVInfo(const G4LogicalVolume* lv) const
0698 {
0699 if (fInfoMap.find(lv) != fInfoMap.end()) {
0700 return fInfoMap.at(lv);
0701 }
0702 else if (fChemToPhys.find(lv) != fChemToPhys.end()) {
0703 return fInfoMap.at(fChemToPhys.at(lv));
0704 }
0705 else {
0706 return nullptr;
0707 }
0708 }
0709
0710 void DNAGeometry::PrintOctreeStats()
0711 {
0712 G4cout << "Name Vols EndNodes Max(vols/node)" << G4endl;
0713 G4cout << "----------------------------------------------" << G4endl;
0714 OctreeNode* currentOctree;
0715 for (auto& it : fInfoMap) {
0716 char buffer[80];
0717 currentOctree = it.second->GetOctree();
0718 G4int vols = currentOctree->GetContents().size();
0719 const char* lvname = it.first->GetName().c_str();
0720 G4int mvols = currentOctree->GetMaxContents();
0721 G4int nodes = currentOctree->GetNumberOfTerminalNodes();
0722 std::snprintf(buffer, 80, "%-12s %-7i %-7i %-9i", lvname, vols, nodes, mvols);
0723 G4cout << buffer << G4endl;
0724 }
0725 G4cout << "-------------------------------------------" << G4endl;
0726 }
0727
0728
0729
0730
0731
0732 G4VPhysicalVolume* DNAGeometry::PlacePhosphate(G4LogicalVolume* physicsLogical,
0733 const molecule_t& thisMolecule,
0734 const molecule_t& sugar)
0735 {
0736
0737 auto vattrs = new G4VisAttributes(G4Color::Yellow());
0738 vattrs->SetForceSolid(true);
0739
0740 std::stringstream ss;
0741 ss << thisMolecule.fname << "-" << thisMolecule.fchain_id << "-" << thisMolecule.fstrand_id << "-"
0742 << thisMolecule.fbase_idx;
0743 G4String name = ss.str();
0744
0745 G4RotationMatrix rot;
0746 rot.rotateX(thisMolecule.frotation.getX());
0747 rot.rotateY(thisMolecule.frotation.getY());
0748 rot.rotateZ(thisMolecule.frotation.getZ());
0749
0750 G4VPhysicalVolume* pv_placement;
0751 G4RotationMatrix* rot_pointer;
0752 if (sugar.fname == "wall") {
0753 if (this->GetVerbosity() >= 2) {
0754 G4cout << "Wall placement" << G4endl;
0755 }
0756 G4ThreeVector abspos(std::abs(thisMolecule.fposition.getX()),
0757 std::abs(thisMolecule.fposition.getY()),
0758 std::abs(thisMolecule.fposition.getZ()));
0759 G4ThreeVector overlaps = abspos + thisMolecule.fsize - fVoxelSideLength;
0760
0761 G4Ellipsoid* mol;
0762
0763
0764 G4double z_cut = -1 * utility::Min(overlaps);
0765 if (z_cut < 0)
0766 {
0767 mol = new G4Ellipsoid(name, thisMolecule.fsize.getX(), thisMolecule.fsize.getY(),
0768 thisMolecule.fsize.getZ());
0769 }
0770 else
0771 {
0772 mol = new G4Ellipsoid(name, thisMolecule.fsize.getX(), thisMolecule.fsize.getY(),
0773 thisMolecule.fsize.getZ(), -thisMolecule.fsize.getZ(), z_cut);
0774 }
0775
0776 rot_pointer = new G4RotationMatrix(rot.inverse());
0777 auto molLogic = new G4LogicalVolume(mol, fpPhosphateMaterial, name);
0778
0779 molLogic->SetVisAttributes(vattrs);
0780 pv_placement = new G4PVPlacement(rot_pointer, thisMolecule.fposition, molLogic, name,
0781 physicsLogical, false, 0, this->GetOverlaps());
0782 }
0783 else {
0784
0785 G4ThreeVector z_direction = sugar.fposition - thisMolecule.fposition;
0786 G4double separation = z_direction.mag();
0787 G4double z_cut = separation - sugar.fsize.getX();
0788
0789 G4ThreeVector local_z = G4ThreeVector(0, 0, 1);
0790 G4ThreeVector z_new = z_direction / z_direction.mag();
0791
0792 G4ThreeVector ortho = local_z.cross(z_new);
0793 G4double dp = local_z.dot(z_new);
0794 G4double angle = -std::atan2(ortho.mag(), dp);
0795
0796 G4RotationMatrix second_rot(ortho, angle);
0797 rot_pointer = new G4RotationMatrix(second_rot);
0798
0799 auto mol = new G4Ellipsoid(name, thisMolecule.fsize.getX(), thisMolecule.fsize.getY(),
0800 thisMolecule.fsize.getZ(), -thisMolecule.fsize.getZ(), z_cut);
0801
0802 auto molLogic = new G4LogicalVolume(mol, fpPhosphateMaterial, name);
0803
0804 molLogic->SetVisAttributes(vattrs);
0805 pv_placement = new G4PVPlacement(rot_pointer, thisMolecule.fposition, molLogic, name,
0806 physicsLogical, false, 0, this->GetOverlaps());
0807 }
0808
0809 return pv_placement;
0810 }
0811
0812 G4VPhysicalVolume* DNAGeometry::PlaceSugar(G4LogicalVolume* physicsLogical,
0813 const molecule_t& thisMolecule,
0814 const molecule_t& phosphate)
0815 {
0816
0817 auto vattrs = new G4VisAttributes(G4Color::Red());
0818 vattrs->SetForceSolid(true);
0819
0820
0821 std::stringstream ss;
0822 ss << thisMolecule.fname << "-" << thisMolecule.fchain_id << "-" << thisMolecule.fstrand_id << "-"
0823 << thisMolecule.fbase_idx;
0824 G4String name = ss.str();
0825
0826 G4RotationMatrix rot;
0827 rot.rotateX(thisMolecule.frotation.getX());
0828 rot.rotateY(thisMolecule.frotation.getY());
0829 rot.rotateZ(thisMolecule.frotation.getZ());
0830
0831 G4VPhysicalVolume* pv_placement;
0832 G4RotationMatrix* rot_pointer;
0833 if (phosphate.fname == "wall") {
0834 if (this->GetVerbosity() >= 2) {
0835 G4cout << "Wall placement" << G4endl;
0836 }
0837 G4ThreeVector abspos(std::abs(thisMolecule.fposition.getX()),
0838 std::abs(thisMolecule.fposition.getY()),
0839 std::abs(thisMolecule.fposition.getZ()));
0840 G4ThreeVector overlaps = abspos + thisMolecule.fsize - fVoxelSideLength;
0841
0842 G4Ellipsoid* mol;
0843
0844
0845 G4double z_cut = -1 * utility::Min(overlaps);
0846 if (z_cut < 0)
0847 {
0848 mol = new G4Ellipsoid(name, thisMolecule.fsize.getX(), thisMolecule.fsize.getY(),
0849 thisMolecule.fsize.getZ());
0850 }
0851 else
0852 {
0853 mol = new G4Ellipsoid(name, thisMolecule.fsize.getX(), thisMolecule.fsize.getY(),
0854 thisMolecule.fsize.getZ(), -thisMolecule.fsize.getZ(), z_cut);
0855 }
0856
0857 rot_pointer = new G4RotationMatrix(rot.inverse());
0858 auto molLogic = new G4LogicalVolume(mol, fpSugarMaterial, name);
0859
0860 molLogic->SetVisAttributes(vattrs);
0861 pv_placement = new G4PVPlacement(rot_pointer, thisMolecule.fposition, molLogic, name,
0862 physicsLogical, false, 0, this->GetOverlaps());
0863 }
0864 else {
0865
0866 G4ThreeVector z_direction = phosphate.fposition - thisMolecule.fposition;
0867 G4double separation = z_direction.mag();
0868 G4double z_cut = separation - phosphate.fsize.getX();
0869
0870 G4ThreeVector local_z = G4ThreeVector(0, 0, 1);
0871 G4ThreeVector z_new = z_direction / z_direction.mag();
0872
0873 G4ThreeVector ortho = local_z.cross(z_new);
0874 G4double dp = local_z.dot(z_new);
0875 G4double angle = -std::atan2(ortho.mag(), dp);
0876
0877 G4RotationMatrix first_rot(ortho, angle);
0878 rot_pointer = new G4RotationMatrix(first_rot);
0879
0880 auto mol = new G4Ellipsoid(name, thisMolecule.fsize.getX(), thisMolecule.fsize.getY(),
0881 thisMolecule.fsize.getZ(), -thisMolecule.fsize.getZ(), z_cut);
0882
0883 auto molLogic = new G4LogicalVolume(mol, fpSugarMaterial, name);
0884
0885 molLogic->SetVisAttributes(vattrs);
0886
0887 pv_placement = new G4PVPlacement(rot_pointer, thisMolecule.fposition, molLogic, name,
0888 physicsLogical, false, 0, this->GetOverlaps());
0889 }
0890 return pv_placement;
0891 }
0892
0893
0894
0895 G4VPhysicalVolume* DNAGeometry::PlaceBase(G4LogicalVolume* physicsLogical,
0896 const molecule_t& thisMolecule,
0897 const molecule_t& oppBasePair,
0898 const molecule_t& nextBasePair, const molecule_t& sugar)
0899 {
0900 std::stringstream ss;
0901 ss << thisMolecule.fname << "-" << thisMolecule.fchain_id << "-" << thisMolecule.fstrand_id << "-"
0902 << thisMolecule.fbase_idx;
0903 G4String name = ss.str();
0904
0905 G4RotationMatrix rot;
0906 rot.rotateX(thisMolecule.frotation.getX());
0907 rot.rotateY(thisMolecule.frotation.getY());
0908 rot.rotateZ(thisMolecule.frotation.getZ());
0909
0910 G4ThreeVector sugar_dirn = sugar.fposition - thisMolecule.fposition;
0911 G4ThreeVector oppBPdirn = oppBasePair.fposition - thisMolecule.fposition;
0912 G4ThreeVector nextBPdirn = nextBasePair.fposition - thisMolecule.fposition;
0913
0914 G4double sugar_cut = sugar_dirn.mag() - sugar.fsize.getX();
0915
0916 G4double base_scaling = oppBPdirn.mag() - oppBasePair.fsize.getY();
0917 base_scaling = std::min(1., base_scaling / thisMolecule.fsize.getY());
0918
0919
0920 G4double thisShapeX = base_scaling * thisMolecule.fsize.getX();
0921 G4double thisShapeY = base_scaling * thisMolecule.fsize.getY();
0922 G4double thisShapeZ = base_scaling * thisMolecule.fsize.getZ();
0923 thisShapeZ = std::min(thisShapeZ, 1.7 * angstrom);
0924
0925
0926 G4ThreeVector z_old = G4ThreeVector(0, 0, 1);
0927 G4ThreeVector z_new = sugar_dirn / sugar_dirn.mag();
0928
0929 G4ThreeVector ortho = z_old.cross(z_new);
0930 G4double angle = std::acos(z_new.dot(z_old));
0931
0932
0933 G4RotationMatrix first_rot;
0934 G4RotationMatrix first_rot_1(ortho, angle);
0935 G4RotationMatrix first_rot_2(ortho, angle + halfpi);
0936 G4RotationMatrix first_rot_3(ortho, angle + pi);
0937 G4RotationMatrix first_rot_4(ortho, angle + 3 * halfpi);
0938
0939 if (first_rot_1(z_old).dot(z_new) > 0.99) {
0940 first_rot = first_rot_1;
0941 }
0942 else if (first_rot_2(z_old).dot(z_new) > 0.99) {
0943 first_rot = first_rot_2;
0944 }
0945 else if (first_rot_3(z_old).dot(z_new) > 0.99) {
0946 first_rot = first_rot_3;
0947 }
0948 else if (first_rot_4(z_old).dot(z_new) > 0.99) {
0949 first_rot = first_rot_4;
0950 }
0951 else {
0952 G4cout << "Could not identify first rotation" << G4endl;
0953 }
0954
0955
0956
0957
0958 G4ThreeVector y = first_rot(G4ThreeVector(0, 1, 0));
0959 G4ThreeVector up = rot(G4ThreeVector(0, 0, 1));
0960
0961 G4double interval = 0.1;
0962 G4double theta = -2 * interval;
0963 G4double prev1 = -DBL_MAX;
0964 G4double prev2 = -DBL_MAX;
0965 G4double val = -DBL_MAX;
0966 G4RotationMatrix z_rotation(z_new, theta);
0967 while (theta <= twopi + interval) {
0968 prev2 = prev1;
0969 prev1 = val;
0970 theta += interval;
0971 z_rotation.rotate(interval, z_new);
0972 val = z_rotation(y).dot(up);
0973 if ((prev1 < val) && (prev1 < prev2)) {
0974
0975 theta -= interval;
0976 break;
0977 }
0978 }
0979 G4RotationMatrix second_rot(first_rot.inverse()(z_new), theta);
0980
0981 auto rot_pointer = new G4RotationMatrix(second_rot.inverse() * first_rot.inverse());
0982 auto mol = new G4Ellipsoid(name, thisShapeX, thisShapeZ, thisShapeY, -thisShapeY, sugar_cut);
0983
0984 G4LogicalVolume* molLogic = nullptr;
0985 G4Color color;
0986 if (thisMolecule.fname == "Guanine") {
0987 color = G4Color::Green();
0988 molLogic = new G4LogicalVolume(mol, fpGuanineMaterial, name);
0989 }
0990 else if (thisMolecule.fname == "Adenine") {
0991 color = G4Color::Blue();
0992 molLogic = new G4LogicalVolume(mol, fpAdenineMaterial, name);
0993 }
0994 else if (thisMolecule.fname == "Cytosine") {
0995 color = G4Color::Cyan();
0996 molLogic = new G4LogicalVolume(mol, fpCytosineMaterial, name);
0997 }
0998 else if (thisMolecule.fname == "Thymine") {
0999 color = G4Color::Magenta();
1000 molLogic = new G4LogicalVolume(mol, fpThymineMaterial, name);
1001 }
1002 else {
1003 G4ExceptionDescription errmsg;
1004 errmsg << "Molecule name: " << thisMolecule.fname << " is invalid." << G4endl << "Aborting"
1005 << G4endl;
1006 G4Exception("DNAGeometry::PlaceBase", "Invalid molecule name", FatalException, errmsg);
1007 }
1008
1009
1010 auto vattrs = new G4VisAttributes(color);
1011 vattrs->SetForceSolid(true);
1012 molLogic->SetVisAttributes(vattrs);
1013
1014 G4VPhysicalVolume* pv_placement =
1015 new G4PVPlacement(rot_pointer, thisMolecule.fposition, molLogic, name, physicsLogical, false, 0,
1016 this->GetOverlaps());
1017
1018 return pv_placement;
1019 }
1020
1021
1022
1023 G4VPhysicalVolume* DNAGeometry::PlaceHistone(G4LogicalVolume* physicsLogical,
1024 const molecule_t& thisMolecule)
1025 {
1026 G4VPhysicalVolume* pv_placement;
1027
1028
1029 auto vattrs = new G4VisAttributes(G4Color::Gray());
1030 vattrs->SetColor(G4Colour(0, 0, 1, 0.2));
1031 vattrs->SetForceSolid(true);
1032
1033
1034 std::stringstream ss;
1035 ss << thisMolecule.fname << "-" << thisMolecule.fchain_id << "-" << thisMolecule.fstrand_id << "-"
1036 << thisMolecule.fbase_idx;
1037 G4String name = ss.str();
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053 G4RotationMatrix rot;
1054 rot.rotateX(thisMolecule.frotation.getX());
1055 rot.rotateY(thisMolecule.frotation.getY());
1056 rot.rotateZ(thisMolecule.frotation.getZ());
1057
1058 G4RotationMatrix* rot_pointer;
1059 auto mol = new G4Ellipsoid(name, thisMolecule.fsize.getX(), thisMolecule.fsize.getY(),
1060 thisMolecule.fsize.getZ());
1061
1062 fHistoneInteractionRadius = thisMolecule.fsize.mag();
1063
1064 rot_pointer = new G4RotationMatrix(rot.inverse());
1065
1066 auto molLogic = new G4LogicalVolume(mol, fpHistoneMaterial, name);
1067 molLogic->SetVisAttributes(vattrs);
1068
1069 pv_placement = new G4PVPlacement(rot_pointer, thisMolecule.fposition, molLogic, name,
1070 physicsLogical, false, 0, this->GetOverlaps());
1071
1072 return pv_placement;
1073 }
1074
1075
1076
1077
1078 void DNAGeometry::FindNearbyMolecules(const G4LogicalVolume* lv, const G4ThreeVector& localPosition,
1079 std::vector<G4VPhysicalVolume*>& daughters_pv_out,
1080 double searchRange)
1081 {
1082 const PlacementVolumeInfo* pvInfo = GetPVInfo(lv);
1083 if (pvInfo == nullptr) {
1084 return;
1085 }
1086
1087 searchRange = std::min(searchRange, fRadicalKillDistance);
1088 OctreeNode* octreeNode = pvInfo->GetOctree();
1089 octreeNode->SearchOctree(localPosition, daughters_pv_out, searchRange);
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101 }
1102
1103
1104
1105 G4bool DNAGeometry::IsInsideHistone(const G4LogicalVolume* lv,
1106 const G4ThreeVector& localPosition) const
1107 {
1108 const PlacementVolumeInfo* pvInfo = GetPVInfo(lv);
1109 if (pvInfo == nullptr) {
1110 G4cout << " Warning : no PV for IsInsideHistone " << G4endl;
1111 return false;
1112 }
1113
1114 if (!fUseHistoneScav) {
1115 return false;
1116 }
1117
1118 OctreeNode* octreeNode = pvInfo->GetHistoneOctree();
1119 const std::vector<G4VPhysicalVolume*> result =
1120 octreeNode->SearchOctree(localPosition, fHistoneInteractionRadius);
1121 return !result.empty();
1122 }
1123
1124
1125
1126 void DNAGeometry::BasePairIndexTest()
1127 {
1128 G4cout << "------------------------" << G4endl;
1129 G4cout << "Begin BasePairIndexTest" << G4endl;
1130 G4bool pass = true;
1131 for (G4int ii = 0; ii != GetNumberOfChains(); ii++) {
1132 G4cout << "Testing Chain " << ii << G4endl;
1133 std::vector<int64_t> test_vector;
1134
1135 int64_t start_idx;
1136 int64_t end_idx;
1137
1138 for (G4int jj = 0; jj != (G4int)fPlacementTransformations.size(); jj++) {
1139 start_idx = GetStartIdx(jj, ii);
1140 end_idx = GetEndIdx(jj, ii);
1141
1142 test_vector.push_back(start_idx);
1143 test_vector.push_back(end_idx);
1144 }
1145 for (auto it = test_vector.begin(); it != test_vector.end(); it++) {
1146 if (it == test_vector.begin())
1147 {
1148 if (it + 1 != test_vector.end()) {
1149 if (*it > *(it + 1)) {
1150 G4ExceptionDescription errmsg;
1151 errmsg << "BasePairIndexTest fails at start of test. "
1152 << "The index " << *(it + 1) << " occurs after " << (*it) << "." << G4endl;
1153 G4Exception("DNAGeometry::BasePairIndexTest", "ERR_TEST_FAIL", FatalException, errmsg);
1154 }
1155 }
1156 }
1157 else if (it == test_vector.end() - 1)
1158 {
1159 if (*it < *(it - 1)) {
1160 G4ExceptionDescription errmsg;
1161 errmsg << "BasePairIndexTest fails. "
1162 << "The index " << *(it - 1) << " occurs before " << (*it) << "." << G4endl;
1163 G4Exception("DNAGeometry::BasePairIndexTest", "ERR_TEST_FAIL", FatalException, errmsg);
1164 }
1165 }
1166 else
1167 {
1168 if (*it < *(it - 1)) {
1169 G4ExceptionDescription errmsg;
1170 errmsg << "BasePairIndexTest fails. "
1171 << "The index " << *(it - 1) << " occurs before " << (*it) << "." << G4endl;
1172 G4Exception("DNAGeometry::BasePairIndexTest", "ERR_TEST_FAIL", FatalException, errmsg);
1173 }
1174 if (*it > *(it + 1)) {
1175 G4ExceptionDescription errmsg;
1176 errmsg << "BasePairIndexTest fails. "
1177 << "The index " << *(it + 1) << " occurs after " << (*it) << "." << G4endl;
1178 G4Exception("DNAGeometry::BasePairIndexTest", "ERR_TEST_FAIL", FatalException, errmsg);
1179 }
1180 }
1181 }
1182 }
1183 if (pass) {
1184 G4cout << "BasePairIndexTest passed" << G4endl;
1185 G4cout << "------------------------" << G4endl;
1186 }
1187 }
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199 int64_t DNAGeometry::GetGlobalUniqueID(G4VPhysicalVolume* dnavol, const G4VTouchable* touch) const
1200 {
1201 const G4String& dnaName = dnavol->GetName();
1202 std::array<G4String, 4> pv_arr = utility::Get_four_elements(dnaName, '-');
1203 molecule mol = utility::GetMoleculeEnum(pv_arr.at(0));
1204 G4int chainIdx = std::stoi(pv_arr.at(1));
1205 G4int strandIdx = std::stoi(pv_arr.at(2));
1206 int64_t baseIdx = std::stoll(pv_arr.at(3));
1207
1208
1209 const G4String& placementName = touch->GetVolume()->GetName();
1210 G4int placeIdx = std::stoi(utility::Get_seperated_element(placementName, '-', 1));
1211
1212 G4int chains = this->GetNumberOfChains();
1213 G4int placements = this->GetNumberOfPlacements();
1214 G4int molecules = ::molecule::Count;
1215
1216
1217
1218
1219 chainIdx = this->GetGlobalChain(placeIdx, chainIdx);
1220
1221 if (this->GetStrandsFlipped(placeIdx)) {
1222 strandIdx = (strandIdx + 1) % 2;
1223 }
1224
1225 int64_t val1 = mol;
1226 int64_t val2 = molecules * placeIdx;
1227 int64_t val3 = molecules * placements * strandIdx;
1228 int64_t val4 = molecules * placements * 2 * chainIdx;
1229 int64_t val5 = molecules * placements * 2 * chains * baseIdx;
1230 int64_t sum = val1 + val2 + val3 + val4 + val5;
1231 if (sum < 0) {
1232 G4ExceptionDescription errmsg;
1233 errmsg << "v1: " << val1 << G4endl << "v2: " << val2 << G4endl << "v3: " << val3 << G4endl
1234 << "v4: " << val4 << G4endl << "v5: " << val5 << G4endl << "sum: " << sum << G4endl
1235 << "placeIdx: " << placeIdx << G4endl << "placements: " << placements << G4endl
1236 << "strandIdx: " << strandIdx << G4endl << "chainIdx: " << chainIdx << G4endl
1237 << "chains: " << chains << G4endl << "baseIdx: " << baseIdx << G4endl << "mol: " << mol
1238 << G4endl;
1239 G4Exception("DNAGeometry::GetGlobalUniqueID", "ERR_BAD_ID", FatalException, errmsg);
1240 }
1241 return sum;
1242 }
1243
1244
1245
1246
1247 int64_t DNAGeometry::GetGlobalUniqueIDTest(int64_t mol, int64_t placeIdx, int64_t chainIdx,
1248 int64_t strandIdx, int64_t baseIdx) const
1249 {
1250 int64_t chains = this->GetNumberOfChains();
1251 int64_t placements = this->GetNumberOfPlacements();
1252 int64_t molecules = ::molecule::Count;
1253
1254
1255
1256
1257 chainIdx = this->GetGlobalChain(placeIdx, chainIdx);
1258 if (this->GetStrandsFlipped(placeIdx)) {
1259 strandIdx = (strandIdx + 1) % 2;
1260 }
1261
1262 int64_t val1 = mol;
1263 int64_t val2 = molecules * placeIdx;
1264 int64_t val3 = molecules * placements * strandIdx;
1265 int64_t val4 = molecules * placements * 2 * chainIdx;
1266 int64_t val5 = molecules * placements * 2 * chains * baseIdx;
1267 int64_t sum = val1 + val2 + val3 + val4 + val5;
1268 if (sum < 0) {
1269 G4ExceptionDescription errmsg;
1270 errmsg << "v1: " << val1 << G4endl << "v2: " << val2 << G4endl << "v3: " << val3 << G4endl
1271 << "v4: " << val4 << G4endl << "v5: " << val5 << G4endl << "sum: " << sum << G4endl
1272 << " molecules : " << molecules << G4endl << "placeIdx: " << placeIdx << G4endl
1273 << "placements: " << placements << G4endl << "strandIdx: " << strandIdx << G4endl
1274 << "chainIdx: " << chainIdx << G4endl << "chains: " << chains << G4endl
1275 << "baseIdx: " << baseIdx << G4endl << "mol: " << mol << G4endl;
1276 G4Exception("DNAGeometry::GetGlobalUniqueIDTest",
1277 "ERR_BAD_ID", FatalException, errmsg);
1278 }
1279 return sum;
1280 }
1281
1282 void DNAGeometry::UniqueIDTest()
1283 {
1284 G4cout << "Unique ID Test starting." << G4endl;
1285 G4int molecules = ::molecule::Count;
1286
1287 G4int chains = this->GetNumberOfChains();
1288 G4int placements = this->GetNumberOfPlacements();
1289 G4int basepairs = 100000;
1290
1291 G4bool pass = true;
1292 G4int counter = 0;
1293 for (G4int pment = 0; pment != (placements); ++pment) {
1294 for (G4int chain = 0; chain != (chains); ++chain) {
1295 for (G4int mol = 0; mol != (molecules); ++mol) {
1296
1297 for (int64_t ii = 0; ii != 10; ++ii) {
1298 counter++;
1299 G4int strand = G4UniformRand() > 0.5 ? 0 : 1;
1300 G4int basepair = (G4int)basepairs * G4UniformRand();
1301 int64_t unique_id =
1302
1303 this->GetGlobalUniqueIDTest(mol, pment, chain, strand, basepair);
1304
1305 G4int real_strand = (this->GetStrandsFlipped(pment)) ?
1306 (strand + 1) % 2
1307 : strand;
1308 G4int real_chain = this->GetGlobalChain(pment, chain);
1309 int64_t real_bp = basepair + this->GetStartIdx(pment, real_chain);
1310
1311
1312
1313 G4int obs_mol = this->GetMoleculeFromUniqueID(unique_id);
1314 G4int obs_placement = this->GetPlacementIndexFromUniqueID(unique_id);
1315 G4int obs_strand = this->GetStrandIDFromUniqueID(unique_id);
1316 G4int obs_chain = this->GetChainIDFromUniqueID(unique_id);
1317 int64_t obs_basepair = this->GetBasePairFromUniqueID(unique_id);
1318
1319
1320
1321 if ((pment != obs_placement) || (real_strand != obs_strand) || (real_chain != obs_chain)
1322 || (obs_mol != mol) || (real_bp != obs_basepair))
1323 {
1324 pass = false;
1325 G4cerr << "Unique ID test failed at ID: " << unique_id << G4endl;
1326 G4cerr << "real_placement: " << pment << " observed: " << obs_placement << G4endl;
1327 G4cerr << "real_strand: " << real_strand << " observed: " << obs_strand << G4endl;
1328 G4cerr << "real_chain: " << real_chain << " observed: " << obs_chain << G4endl;
1329 G4cerr << "real_mol: " << mol << " observed: " << obs_mol << G4endl;
1330 G4cerr << "real_bp: " << real_bp << " observed: " << obs_basepair << G4endl;
1331 }
1332 }
1333 }
1334 }
1335 }
1336 if (!pass) {
1337 G4Exception("DNAGeometry::UniqueIDTest", "ERR_TEST_FAIL", FatalException,
1338 "Algorithm to generate unique ID's failed");
1339 }
1340 G4cout << "Unique ID Test completed, after running " << counter << " tests" << G4endl;
1341 }
1342
1343
1344
1345 G4Material* DNAGeometry::GetMaterialFromUniqueID(int64_t idx) const
1346 {
1347 molecule mol = GetMoleculeFromUniqueID(idx);
1348 switch (mol) {
1349 case ::molecule::SUGAR:
1350 return fpSugarMaterial;
1351 case ::molecule::PHOSPHATE:
1352 return fpPhosphateMaterial;
1353 case ::molecule::GUANINE:
1354 return fpGuanineMaterial;
1355 case ::molecule::ADENINE:
1356 return fpAdenineMaterial;
1357 case ::molecule::THYMINE:
1358 return fpThymineMaterial;
1359 case ::molecule::CYTOSINE:
1360 return fpCytosineMaterial;
1361 case ::molecule::UNSPECIFIED:
1362 G4cout << "DNAGeometry::GetMaterialFromUniqueID: "
1363 << "Encountered an unspecified material." << G4endl;
1364 return fpMediumMaterial;
1365 default:
1366 G4cout << "DNAGeometry::GetMaterialFromUniqueID: "
1367 << "Encountered a bad material enumerator." << G4endl;
1368 return fpMediumMaterial;
1369 }
1370 }
1371
1372
1373
1374 G4int DNAGeometry::GetPlacementIndexFromUniqueID(int64_t idx) const
1375 {
1376
1377 idx -= idx % (int64_t)::molecule::Count;
1378
1379 idx = idx % (int64_t)(::molecule::Count * this->GetNumberOfPlacements());
1380 return (G4int)idx / (::molecule::Count);
1381 }
1382
1383
1384
1385 G4int DNAGeometry::GetStrandIDFromUniqueID(int64_t idx) const
1386 {
1387
1388 idx -= (int64_t)(idx % ::molecule::Count);
1389
1390 idx -= (int64_t)(idx % (::molecule::Count * this->GetNumberOfPlacements()));
1391
1392 idx = (int64_t)(idx % (::molecule::Count * this->GetNumberOfPlacements() * 2));
1393
1394 return (G4int)idx / (::molecule::Count * this->GetNumberOfPlacements());
1395 }
1396
1397
1398
1399 G4int DNAGeometry::GetChainIDFromUniqueID(int64_t idx) const
1400 {
1401
1402 idx -= idx % (int64_t)::molecule::Count;
1403
1404 idx -= idx % (int64_t)(::molecule::Count * this->GetNumberOfPlacements());
1405
1406 idx -= idx % (int64_t)(::molecule::Count * this->GetNumberOfPlacements() * 2);
1407
1408 idx =
1409 idx
1410 % (int64_t)(::molecule::Count * this->GetNumberOfPlacements() * 2 * this->GetNumberOfChains());
1411
1412 G4int chain = (G4int)idx / (int64_t)(::molecule::Count * this->GetNumberOfPlacements() * 2);
1413 return chain;
1414 }
1415
1416
1417
1418 int64_t DNAGeometry::GetBasePairFromUniqueID(int64_t idx) const
1419 {
1420
1421 idx -= idx % ::molecule::Count;
1422
1423
1424 int64_t placement_ = idx % (int64_t)(::molecule::Count * this->GetNumberOfPlacements());
1425 idx -= placement_;
1426
1427
1428 placement_ = placement_ / (int64_t)(::molecule::Count);
1429
1430
1431 idx -= idx % (int64_t)(::molecule::Count * this->GetNumberOfPlacements() * 2);
1432
1433
1434 int64_t chain =
1435 idx
1436 % (int64_t)(::molecule::Count * this->GetNumberOfPlacements() * 2 * this->GetNumberOfChains());
1437 idx -= chain;
1438
1439
1440 chain = chain / (int64_t)(::molecule::Count * this->GetNumberOfPlacements() * 2);
1441
1442
1443 int64_t bp =
1444 idx
1445 / (int64_t)(::molecule::Count * this->GetNumberOfPlacements() * 2 * this->GetNumberOfChains());
1446
1447
1448 bp = bp + this->GetStartIdx(placement_, chain);
1449 return bp;
1450 }
1451
1452
1453
1454 int64_t DNAGeometry::GetMaxBPIdx() const
1455 {
1456 int64_t mx = 0;
1457 for (auto val : std::get<2>(*fPlacementTransformations.end())) {
1458 if (val > mx) {
1459 mx = val;
1460 }
1461 }
1462 return mx;
1463 }
1464
1465
1466
1467 G4int DNAGeometry::GetLocalChain(G4int vol_idx, G4int global_chain) const
1468 {
1469 auto chains = std::get<0>(fPlacementTransformations[vol_idx]);
1470 G4int local = 0;
1471 for (auto chain : chains) {
1472 if (global_chain == chain) {
1473 return local;
1474 }
1475 local++;
1476 }
1477 return -1;
1478 }
1479
1480
1481
1482 G4LogicalVolume* DNAGeometry::GetMatchingPhysVolume(G4LogicalVolume* chem) const
1483 {
1484 auto it = fChemToPhys.find(chem);
1485 if (it != fChemToPhys.end()) {
1486 return it->second;
1487 }
1488 else if (fPhysToChem.find(chem) != fPhysToChem.end()) {
1489 G4cout << "A phyics volume was used to find a chem volume" << G4endl;
1490 return chem;
1491 }
1492 else {
1493 return nullptr;
1494 }
1495 }
1496
1497
1498
1499 G4LogicalVolume* DNAGeometry::GetMatchingChemVolume(G4LogicalVolume* physics) const
1500 {
1501 auto it = fPhysToChem.find(physics);
1502 if (it != fPhysToChem.end()) {
1503 return it->second;
1504 }
1505 else if (fChemToPhys.find(physics) != fChemToPhys.end()) {
1506 G4cout << "A chem volume was used to find a physics volume" << G4endl;
1507 return physics;
1508 }
1509 else {
1510 return nullptr;
1511 }
1512 }
1513
1514 void DNAGeometry::ChromosomeTest()
1515 {
1516 fpChromosomeMapper->Test();
1517 }
1518
1519
1520
1521 void DNAGeometry::AddChangeMoleculeSize(const G4String& name, const G4ThreeVector& size)
1522 {
1523 G4String lower_name = name;
1524
1525 G4StrUtil::to_lower(lower_name);
1526 if (!(fEnableCustomMoleculeSizes)) {
1527 G4cerr << "Custom Molecule Sizes need to be enabled first" << G4endl;
1528 }
1529 else {
1530 if (fMoleculeSizes.count(lower_name) == 0) {
1531
1532 fMoleculeSizes[lower_name] = size;
1533 }
1534 else {
1535
1536 G4cout << lower_name << " is already defined in molecule size, I will update" << G4endl
1537 << " this molecule's size from" << fMoleculeSizes[lower_name] << G4endl << " to "
1538 << size << G4endl;
1539 fMoleculeSizes[lower_name] = size;
1540 }
1541 }
1542 }