Geant4 Cross Reference |
1 // 2 // ******************************************************************** 3 // * License and Disclaimer * 4 // * * 5 // * The Geant4 software is copyright of the Copyright Holders of * 6 // * the Geant4 Collaboration. It is provided under the terms and * 7 // * conditions of the Geant4 Software License, included in the file * 8 // * LICENSE and available at http://cern.ch/geant4/license . These * 9 // * include a list of copyright holders. * 10 // * * 11 // * Neither the authors of this software system, nor their employing * 12 // * institutes,nor the agencies providing financial support for this * 13 // * work make any representation or warranty, express or implied, * 14 // * regarding this software system or assume any liability for its * 15 // * use. Please see the license in the file LICENSE and URL above * 16 // * for the full disclaimer and the limitation of liability. * 17 // * * 18 // * This code implementation is the result of the scientific and * 19 // * technical work of the GEANT4 collaboration. * 20 // * By using, copying, modifying or distributing the software (or * 21 // * any work based on the software) you agree to acknowledge its * 22 // * use in resulting scientific publications, and indicate your * 23 // * acceptance of all terms of the Geant4 Software license. * 24 // ******************************************************************** 25 // 26 // ------------------------------------------------------------- 27 // =============== Begin Documentation Comments =============== 28 //! 29 //! \file FFDetectorConstruction.cc 30 //! \author B. Wendt (brycen.linn.wendt@cern.ch) 31 //! \date June 06, 2014 32 //! 33 //! \brief Implementation of the FFDetectorConstruction class 34 //! 35 //! \details The model simulated is based off a subcritical assembly design 36 //! with 20% enriched meat 37 //! 38 // ================ End Documentation Comments ================ 39 // 40 // Modified: 41 // 42 // 23-06-14 BWendt 43 // Fixed issue with the automatic placement of the meat not working. Solution 44 // was to use the correct units "inch" in the y-direction as well. 45 // Coincidentally eliminated the need to using the 'std::abs()" from the 46 // "cmath" library. 47 // Implemented method "PlaceFuelPlates" 48 // 49 // ------------------------------------------------------------- 50 51 #include "FFDetectorConstruction.hh" 52 53 #include "G4Box.hh" 54 #include "G4Element.hh" 55 #include "G4Isotope.hh" 56 #include "G4LogicalVolume.hh" 57 #include "G4NistManager.hh" 58 #include "G4PVPlacement.hh" 59 #include "G4SystemOfUnits.hh" 60 #include "G4Tubs.hh" 61 #include "G4VPhysicalVolume.hh" 62 #include "globals.hh" 63 64 static const G4double inch = 2.54 * cm; 65 66 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... 67 FFDetectorConstruction::FFDetectorConstruction() : G4VUserDetectorConstruction() 68 { 69 DefineMaterials(); 70 } 71 72 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... 73 G4VPhysicalVolume* FFDetectorConstruction::Construct() 74 { 75 G4ThreeVector position; 76 #ifdef NDEBUG 77 G4bool const overlapChecking = false; 78 #else 79 G4bool const overlapChecking = true; 80 #endif // NDEBUG 81 82 // 83 // Create the world 84 // 85 const G4double worldSize = 40.0 * inch; 86 G4Box* const solidWorld = new G4Box("World", // the name 87 worldSize, // x size 88 worldSize, // y size 89 worldSize); // z size 90 G4LogicalVolume* const logicalWorld = new G4LogicalVolume(solidWorld, // the solid volume 91 fAir, // the material 92 solidWorld->GetName()); // the name 93 // Center at the origin 94 position.set(0.0, 0.0, 0.0); 95 G4VPhysicalVolume* const physicalWorld = 96 new G4PVPlacement(NULL, // no rotation 97 position, // must be at origin 98 logicalWorld, // the logical volume 99 logicalWorld->GetName(), // the name 100 NULL, // no mother volume 101 false, // no boolean ops 102 0, // copy number 103 overlapChecking); // check for overlaps 104 105 // 106 // Create the graphite pile that the subcritical assembly rests on. 107 // 108 const G4double floorH = 30.0 * inch; 109 const G4ThreeVector floorPosition(0.0, 0.0, 0.0); 110 G4Box* const solidFloor = new G4Box("Floor", // the name 111 worldSize, // x size 112 worldSize, // y size 113 floorH * 0.5); // z size 114 G4LogicalVolume* const logicalFloor = new G4LogicalVolume(solidFloor, // the solid volume 115 fGraphite, // the material 116 solidFloor->GetName()); // the name 117 // Shift down so the top is at the origin 118 position.set(0.0, 0.0, -floorH * 0.5); 119 new G4PVPlacement(NULL, // no rotation 120 position, // position 121 logicalFloor, // the logical volume 122 logicalFloor->GetName(), // the name 123 logicalWorld, // the mother volume 124 false, // no boolean ops 125 0, // copy number 126 overlapChecking); // check for overlaps 127 128 // 129 // Create the tank 130 // 131 const G4double tankWallThickness = 0.25 * inch; 132 const G4double tankOR = 18.0 * inch; 133 const G4double tankH = 39.0 * inch; 134 G4Tubs* const solidTank = new G4Tubs("Tank_Wall", // the name 135 0.0, // inner radius 136 tankOR, // outer radius 137 tankH * 0.5, // half height 138 0.0 * deg, // start angle 139 360.0 * deg); // end angle 140 G4LogicalVolume* const logicalTank = new G4LogicalVolume(solidTank, // the solid volume 141 fAluminum, // the material 142 solidTank->GetName()); // the name 143 // Shift up so the base is at the origin 144 position.set(0.0, 0.0, tankH * 0.5); 145 new G4PVPlacement(NULL, // no rotation 146 position, // shift up 147 logicalTank, // the logical volume 148 logicalTank->GetName(), // the name 149 logicalWorld, // the mother volume 150 false, // no boolean ops 151 0, // copy number 152 overlapChecking); // check for overlaps 153 // Top 3 inches are air 154 const G4double tankAirH = 3.0 * inch; 155 G4Tubs* const solidTankAir = new G4Tubs("Tank_Air", // the name 156 0.0, // inner radius 157 tankOR - tankWallThickness, // outer radius 158 tankAirH * 0.5, // half height 159 0.0 * deg, // start angle 160 360.0 * deg); // end angle 161 G4LogicalVolume* const logicalTankAir = new G4LogicalVolume(solidTankAir, // the solid volume 162 fAir, // the material 163 solidTankAir->GetName()); // the name 164 // Shift up so that the top of the air is the same as the top of the tank 165 position.set(0.0, 0.0, (tankH - tankAirH) * 0.5); 166 new G4PVPlacement(NULL, // no rotation 167 position, // shift ip 168 logicalTankAir, // the logical volume 169 logicalTankAir->GetName(), // the name 170 logicalTank, // the mother volume 171 false, // no boolean ops 172 0, // copy number 173 overlapChecking); // check for overlaps 174 // Fill remaining area with water 175 const G4double tankH2OH = (tankH - (tankAirH + tankWallThickness)); 176 G4Tubs* const solidTankH2O = new G4Tubs("Tank_H2O", // the name 177 0.0, // inner radius 178 tankOR - tankWallThickness, // outer radius 179 tankH2OH * 0.5, // half height 180 0.0 * deg, // start angle 181 360.0 * deg); // end angle 182 G4LogicalVolume* const logicalTankH2O = new G4LogicalVolume(solidTankH2O, // the solid volume 183 fAluminum, // the material 184 solidTankH2O->GetName()); // the name 185 // Shift up so that the top of the water is at the bottom of the air 186 const G4double centerOfH2O = (tankH - tankH2OH) * 0.5 - tankAirH; 187 position.set(0.0, 0.0, centerOfH2O); 188 new G4PVPlacement(NULL, // no rotation 189 position, // shift to origin 190 logicalTankH2O, // the logical volume 191 logicalTankH2O->GetName(), // the name 192 logicalTank, // the mother volume 193 false, // no boolean ops 194 0, // copy number 195 overlapChecking); // check for overlaps 196 197 // 198 // Fuel plates 199 // 200 const G4double plateX = 3.0 * inch; 201 const G4double plateY = 0.08 * inch; 202 const G4double plateZ = 26.0 * inch; 203 const G4double meatX = 2.75 * inch; 204 const G4double meatY = 0.04 * inch; 205 const G4double meatZ = 24.0 * inch; 206 const G4double xSpacing = 5.0 * inch; 207 const G4double ySpacing = 0.3 * inch; 208 const G4double plateRadius = 12.0 * inch; 209 // Define the aluminim claddiing 210 G4Box* const solidPlate = new G4Box("Plate_Cladding", // the name 211 plateX * 0.5, // x size 212 plateY * 0.5, // y size 213 plateZ * 0.5); // z size 214 G4LogicalVolume* const logicalPlate = new G4LogicalVolume(solidPlate, // the solid volume 215 fAluminum, // the material 216 solidPlate->GetName()); // the name 217 // Place the meat inside the cladding 218 G4Box* const solidMeat = new G4Box("Plate_Meat", // the name 219 meatX * 0.5, // x size 220 meatY * 0.5, // y size 221 meatZ * 0.5); // z size 222 G4LogicalVolume* const logicalMeat = new G4LogicalVolume(solidMeat, // the solid volume 223 fUO2_20E, // the material 224 solidMeat->GetName()); // the name 225 // The meat goes into the exact center of the plate 226 position.set(0.0, 0.0, 0.0); 227 new G4PVPlacement(NULL, // no rotation 228 position, // position 229 logicalMeat, // the logical volume 230 logicalMeat->GetName(), // the name 231 logicalPlate, // the mother volume 232 false, // no boolean ops 233 0, // copy number 234 overlapChecking); // check for overlaps 235 // The plate will be centered in the z-direction within the water 236 // Simulate a subcritical assembly loading within a radius of 12 inches 237 bool placeMe; 238 239 position.setZ(0.0); 240 fCopyNumber = 0; 241 for (double x = 0.0; x <= plateRadius; x += xSpacing) { 242 // 5 rows of plates 243 for (double y = 0.0; y <= plateRadius; y += ySpacing) { 244 placeMe = false; 245 246 // Fuel plate must be completely within the radius to be placed 247 if (std::sqrt(x * x + y * y) < plateRadius) { 248 // Leave a 1 inch radius opening in the middle for the neutron 249 // source 250 if (std::sqrt(x * x + y * y) > 1.0 * inch) { 251 placeMe = true; 252 } 253 } 254 255 if (placeMe) { 256 PlaceFuelPlate(x, y, logicalPlate, logicalTankH2O); 257 PlaceFuelPlate(x, -y, logicalPlate, logicalTankH2O); 258 if (x > 0.0) { 259 PlaceFuelPlate(-x, y, logicalPlate, logicalTankH2O); 260 PlaceFuelPlate(-x, -y, logicalPlate, logicalTankH2O); 261 } 262 } 263 } 264 } 265 G4cout << fCopyNumber << " plates were added to the subcritical assembly" << G4endl; 266 267 // 268 // Neutron Source 269 // 270 // TODO create the AmBe material in DefineMaterials() and use it here 271 // For now steel is used, but the logical volume is used in the 272 // PrimaryGeneratorAction to know where to emit the neutrons from 273 const G4double sourceH = 2 * inch; 274 const G4double sourceR = 0.2 * inch; 275 G4Tubs* const solidSource = new G4Tubs("NeutronSource", // the name 276 0.0, // inner radius 277 sourceR, // outer radius 278 sourceH * 0.5, // half height 279 0.0 * deg, // start angle 280 360.0 * deg); // end angle 281 G4LogicalVolume* const logicalSource = new G4LogicalVolume(solidSource, // the solid volume 282 fStainlessSteel, // the material 283 solidSource->GetName()); // the name 284 // Place in the exact center of the water tank 285 position.set(0.0, 0.0, 0.0); 286 new G4PVPlacement(NULL, // no rotation 287 position, // shift to origin 288 logicalSource, // the logical volume 289 logicalSource->GetName(), // the name 290 logicalTankH2O, // the mother volume 291 false, // no boolean ops 292 0, // copy number 293 overlapChecking); // check for overlaps 294 295 // 296 // Detector Tower 297 // 298 const G4double polyS = 3.0 * inch; 299 const G4double polyH = 18.0 * inch; 300 G4Box* const solidPoly = new G4Box("Poly", // the name 301 polyS, // x size 302 polyS, // y size 303 polyH); // z size 304 G4LogicalVolume* const logicalPoly = new G4LogicalVolume(solidPoly, // the solid volume 305 fPolyethylene, // the material 306 solidPoly->GetName()); // the name 307 // The polyethylene detector tower goes just outside the tank at 45 deg 308 G4double radiusToPolyCenter = (tankOR / std::sqrt(2.0)) + std::sqrt(2.0) * polyS; 309 position.set(-radiusToPolyCenter, radiusToPolyCenter, polyH); 310 new G4PVPlacement(NULL, // no rotation 311 position, // position 312 logicalPoly, // the logical volume 313 logicalPoly->GetName(), // the name 314 logicalWorld, // the mother volume 315 false, // no boolean ops 316 0, // copy number 317 overlapChecking); // check for overlaps 318 // Create the detector shell 319 G4double shellR = 0.3 * inch; 320 G4double shellH = 6.5 * inch; 321 G4Tubs* const solidShell = new G4Tubs("Detector_Shell", // the name 322 0.0, // inner radius 323 shellR, // outer radius 324 shellH * 0.5, // half height 325 0.0 * deg, // start angle 326 360.0 * deg); // end angle 327 G4LogicalVolume* const logicalShell = new G4LogicalVolume(solidShell, // the solid volume 328 fStainlessSteel, // the material 329 solidShell->GetName()); // the name 330 // Place in the exact center of the polyethylene tower 331 position.set(0.0, 0.0, 0.0); 332 new G4PVPlacement(NULL, // no rotation 333 position, // shift to origin 334 logicalShell, // the logical volume 335 logicalShell->GetName(), // the name 336 logicalPoly, // the mother volume 337 false, // no boolean ops 338 0, // copy number 339 overlapChecking); // check for overlaps 340 // Create the BF3 detector 341 G4double BF3R = 0.2 * inch; 342 G4double BF3H = 6.0 * inch; 343 G4Tubs* const solidBF3 = new G4Tubs("Detector_BF3_Core", // the name 344 0.0, // inner radius 345 BF3R, // outer radius 346 BF3H * 0.5, // half height 347 0.0 * deg, // start angle 348 360.0 * deg); // end angle 349 G4LogicalVolume* const logicalBF3 = new G4LogicalVolume(solidBF3, // the solid volume 350 fBF3_96E, // the material 351 solidBF3->GetName()); // the name 352 // Place in the exact center of the shell 353 position.set(0.0, 0.0, 0.0); 354 new G4PVPlacement(NULL, // no rotation 355 position, // shift to origin 356 logicalBF3, // the logical volume 357 logicalBF3->GetName(), // the name 358 logicalShell, // the mother volume 359 false, // no boolean ops 360 0, // copy number 361 overlapChecking); // check for overlaps 362 363 return physicalWorld; 364 } 365 366 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... 367 void FFDetectorConstruction::DefineMaterials(void) 368 { 369 static G4NistManager* const nist = G4NistManager::Instance(); 370 371 fAir = nist->FindOrBuildMaterial("G4_AIR"); 372 fAluminum = nist->FindOrBuildMaterial("G4_Al"); 373 fGraphite = nist->FindOrBuildMaterial("G4_GRAPHITE"); 374 fPolyethylene = nist->FindOrBuildMaterial("G4_POLYETHYLENE"); 375 fStainlessSteel = nist->FindOrBuildMaterial("G4_STAINLESS-STEEL"); 376 fWater = nist->FindOrBuildMaterial("G4_WATER"); 377 378 /*// List available materials 379 std::vector< G4String > materials = nist->GetNistMaterialNames(); 380 for(unsigned int i = 0; 381 i < materials.size(); 382 ++i) 383 { 384 G4cout << materials[i] << G4endl; 385 }*/ 386 387 // 388 // Define the 20% enriched UO2 389 // 390 // First we need to start by creating the isotopes 391 G4double const U235Enrichment = 0.2; 392 G4double const U238Enrichment = 0.8; 393 G4Isotope* const iU235 = new G4Isotope("iU235", // name 394 92, // ZZZ 395 235, // AAA 396 235.053930 * (g / mole)); // molecular weight 397 G4Isotope* const iU238 = new G4Isotope("iU238", // name 398 92, // ZZZ 399 238, // AAA 400 238.050788 * (g / mole)); // molecular weight 401 // Now create the elements and add the isotopes 402 G4Element* const U235 = new G4Element("U235", // name 403 "U235", // symbol 404 1); // number of isotopes 405 U235->AddIsotope(iU235, // isotope 406 1.0); // abundance 407 G4Element* const U238 = new G4Element("U238", // name 408 "U238", // symbol 409 1); // number of isotopes 410 U238->AddIsotope(iU238, // isotope 411 1.0); // abundance 412 G4Element* const oxygen = nist->FindOrBuildElement("O"); 413 // Calculate the mass fractions 414 const G4double UO2MolecularWeight = 415 U235->GetA() * U235Enrichment + U238->GetA() * U238Enrichment + oxygen->GetA() * 2; 416 const G4double U235MassFraction = (U235->GetA() * U235Enrichment) / UO2MolecularWeight; 417 const G4double U238MassFraction = (U238->GetA() * U238Enrichment) / UO2MolecularWeight; 418 const G4double oxygenMassFraction = (oxygen->GetA() * 2) / UO2MolecularWeight; 419 // create the material and add the elements 420 fUO2_20E = new G4Material("UO2_20E", // name 421 10.97 * (g / cm3), // density 422 3); // number of components 423 fUO2_20E->AddElement(U235, // element 424 U235MassFraction); // mass fraction 425 fUO2_20E->AddElement(U238, // element 426 U238MassFraction); // mass fraction 427 fUO2_20E->AddElement(oxygen, // element 428 oxygenMassFraction); // mass fraction 429 430 // 431 // Define the BF3 432 // 433 // The BF3 is B-10 enriched 434 // http://www.orau.org/ptp/collection/proportional%20counters/bf3info.htm 435 G4double const B10Enrichment = 0.96; 436 G4double const B11Enrichment = 0.04; 437 G4Isotope* const iB10 = new G4Isotope("iB10", // name 438 5, // ZZZ 439 10, // AAA 440 10.0129370 * (g / mole)); // molecular weight 441 G4Isotope* const iB11 = new G4Isotope("iB11", // name 442 5, // ZZZ 443 11, // AAA 444 11.0093054 * (g / mole)); // molecular weight 445 // Now create the elements and add the isotopes 446 G4Element* const B10 = new G4Element("B10", // name 447 "B10", // symbol 448 1); // number of isotopes 449 B10->AddIsotope(iB10, // isotope 450 1.0); // abundance 451 G4Element* const B11 = new G4Element("B11", // name 452 "B11", // symbol 453 1); // number of isotopes 454 B11->AddIsotope(iB11, // isotope 455 1.0); // abundance 456 G4Element* const flouride = nist->FindOrBuildElement("F"); 457 // Calculate the mass fractions 458 const G4double BF3MolecularWeight = 459 B10->GetA() * B10Enrichment + B11->GetA() * B11Enrichment + flouride->GetA() * 3; 460 const G4double B10MassFraction = (B10->GetA() * B10Enrichment) / BF3MolecularWeight; 461 const G4double B11MassFraction = (B11->GetA() * B11Enrichment) / BF3MolecularWeight; 462 const G4double flourideMassFraction = (flouride->GetA() * 3) / BF3MolecularWeight; 463 // create the material and add the elements 464 fBF3_96E = new G4Material("BF3_96E", // name 465 2.5 * (kg / m3), // density 466 3); // number of components 467 fBF3_96E->AddElement(B10, // element 468 B10MassFraction); // mass fraction 469 fBF3_96E->AddElement(B11, // element 470 B11MassFraction); // mass fraction 471 fBF3_96E->AddElement(flouride, // element 472 flourideMassFraction); // mass fraction 473 } 474 475 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... 476 void FFDetectorConstruction::PlaceFuelPlate(double x, double y, 477 G4LogicalVolume* const myLogicalVolume, 478 G4LogicalVolume* const parentLogicalVolume) 479 { 480 G4ThreeVector position(x, y); 481 std::ostringstream copyName; 482 copyName << "Plate@Location X:" << std::setprecision(2) << x / inch << " Y:" << y / inch; 483 484 new G4PVPlacement(NULL, // no rotation 485 position, // position 486 myLogicalVolume, // the logical volume 487 copyName.str(), // the name 488 parentLogicalVolume, // the mother volume 489 false, // no boolean ops 490 fCopyNumber++, // copy number 491 true); // check for overlaps 492 } 493 494 //....oooOO0OOooo........oooOO0OOooo........oooOO0OOooo........oooOO0OOooo...... 495 FFDetectorConstruction::~FFDetectorConstruction() 496 { 497 // Nothing here 498 } 499