Creating a G4UnionSolid with 10 different components

I am trying to make a volume using mulitple individual parts (3 cylindrical rods and 7 circular rings). I know I can put 2 parts together using G4UnionSolid but I am not sure how to place all 7 rings and the 3 rods in the parameters of G4UnionSolid class.

Hello,

Combine part 1 and part 2 to part “A”.
Combine part A and part 3 to part “B”.
Combine part B and part 4 to part “C”.
… and so on …

As far as i know G4UnionSolid can only take two parts to combine.

Regards
Max

That’s what I thought. But I wondered if there is a smart way of doing it

Take a look at G4MultiUnion.hh. It let’s you supply a whole list of solids which all get unioned together in one step. That avoids the significant performance loss from cascaded boolean solids.

1 Like
	G4Tubs* ring1 = new G4Tubs("ring1", 129*mm, 134*mm, 2/2*mm, 0, 2*pi);
	G4Tubs* ring2 = new G4Tubs("ring2", 129*mm, 134*mm, 2/2*mm, 0, 2*pi);
	G4Tubs* ring3 = new G4Tubs("ring3", 129*mm, 134*mm, 2/2*mm, 0, 2*pi);
	G4Tubs* ring4 = new G4Tubs("ring4", 129*mm, 134*mm, 2/2*mm, 0, 2*pi);
	G4Tubs* ring5 = new G4Tubs("ring5", 129*mm, 134*mm, 2/2*mm, 0, 2*pi);
	G4Tubs* ring6 = new G4Tubs("ring6", 129*mm, 134*mm, 2/2*mm, 0, 2*pi);
	G4Tubs* ring7 = new G4Tubs("ring7", 129*mm, 134*mm, 2/2*mm, 0, 2*pi);

  G4Tubs* rod1 = new G4Tubs("rod1", 0.0*mm, 2.5*mm, 1400/2*mm, 0, 2*pi);
	G4Tubs* rod2 = new G4Tubs("rod2", 0.0*mm, 2.5*mm, 1400/2*mm, 0, 2*pi);
	G4Tubs* rod3 = new G4Tubs("rod3", 0.0*mm, 2.5*mm, 1400/2*mm, 0, 2*pi);
    
  G4UnionSolid* ring12 = new G4UnionSolid("ring12", ring1, ring2, 0, G4ThreeVector(0, 0, -300*mm));
	G4UnionSolid* ring123 = new G4UnionSolid("ring123", ring12, ring3, 0, G4ThreeVector(0, 0, -600*mm));
	G4UnionSolid* ring1234 = new G4UnionSolid("ring1234", ring123, ring4, 0, G4ThreeVector(0, 0, -700*mm));
	G4UnionSolid* ring12345 = new G4UnionSolid("ring12345", ring1234, ring5, 0, G4ThreeVector(0, 0, 300*mm));
	G4UnionSolid* ring123456 = new G4UnionSolid("ring123456", ring12345, ring6, 0, G4ThreeVector(0, 0, 600*mm));
	G4UnionSolid* ring1234567 = new G4UnionSolid("ring1234567", ring123456, ring7, 0, G4ThreeVector(0, 0, 700*mm));

	G4UnionSolid* ring1234567rod1 = new G4UnionSolid("ring1234567rod1", ring1234567, rod1, 0, G4ThreeVector(134.0*mm, 0.0*mm, 0*mm));
	G4UnionSolid* ring1234567rod2 = new G4UnionSolid("ring1234567rod1", ring1234567rod1, rod2, 0, G4ThreeVector(-67.0*mm, 116.047*mm, 0*mm));
	G4UnionSolid* ring1234567rod3 = new G4UnionSolid("ring1234567rod1", ring1234567rod2, rod3, 0, G4ThreeVector(-67.0*mm, -116.047*mm, 0*mm));

This is what I ended up doing. It works but very inefficiently. I tried implementing G4MultiUnion but that gave me errors and I couldn’t fix it then

Nested unions like that are terribly inefficient; that’s why G4MultiUnion was developed. It looks to me like you aren’t apply any rotations to your objects, just positional placements. That should make it much easier to deal with.

With G4MultiUnion the very first solid on the list defines the coordinate system (x,y,z orientation and the location of (0,0,0)). All of these other solids should have their placements done relative to that first one, and when you place the whole solid, the placement specifies where that origin (0,0,0) goes.

It looks like that’s exactly what you’re doing. If I understand right, your “ring1” is the one in the center along the axis. Then ring2, ring3, ring4 are sequentially farther away along -z, and ring5, ring6, ring7 are sequentially along +z.

So with G4MultiUnion, you would do something like:

    G4MultiUnion* railgun = new G4MultiUnion("railgun");
    railgun->AddNode(ring1, G4Transform3D::Identity);
    railgun->AddNode(ring2, G4TranslateZ3D(-300*mm);
    railgun->AddNode(ring3, G4TranslateZ3D(-600*mm);
    railgun->AddNode(ring4, G4TranslateZ3D(-700*mm);
    railgun->AddNode(ring5, G4TranslateZ3D(300*mm);
    railgun->AddNode(ring6, G4TranslateZ3D(600*mm);
    railgun->AddNode(ring7, G4TranslateZ3D(700*mm);
    railgun->AddNode(rod1, G4Translate3D(134.0*mm, 0.0*mm, 0*mm);
    railgun->AddNode(rod2, G4Translate3D(-67.0*mm, 116.047*mm, 0*mm);
    railgun->AddNode(rod3, G4Translate3D(-67.0*mm, -116.047*mm, 0*mm);
1 Like

Remark 1. In your case all rings and rods are the same. It is not necessary to construct a separate volume for each ring and each rod.

Remark 2. Geant4 is not a CAD system, it’s not a good practice to apply a CAD approach to construction of geometry in Geant4. Boolean operations in Geant4 are rather CPU demanding, so it’s better to avoid them. For example in your case the geometry can be defined as a set of separate rings and rods without any impact on the simulation results:

  // Construct ring                                                                                                     
  G4double ringRMin = 129*mm, ringRMax = 134*mm, ringThickness = 2.*mm;
  G4Tubs* ringSolid = new G4Tubs("Ring", ringRMin, ringRMax, ringThickness/2., 0., twopi);
  G4LogicalVolume* ringLogical = new G4LogicalVolume(ringSolid, ringMaterial, "Ring");

  // Place rings                                                                                                        
  G4double ringZPositions[7] = { -700*mm, -600*mm, -300*mm, 0*mm, 300*mm, 600*mm, 700*mm };
  for (G4int i = 0; i < 7; ++i) {
    new G4PVPlacement(nullptr, G4ThreeVector(0., 0., ringZPositions[i]),
                      ringLogical, "Ring", logicEnv,
                      false, 0, checkOverlaps);
  }

  // Constrcut short and long rods                                                                                      
  G4double rodR = 2.5*mm;
  G4double shortRodLength = ringZPositions[1] - ringZPositions[0] - ringThickness;
  G4double longRodLength  = ringZPositions[2] - ringZPositions[1] - ringThickness;
  G4Tubs* shortRodSolid = new G4Tubs("Rod", 0*mm, rodR, shortRodLength/2., 0, twopi);
  G4Tubs* longRodSolid = new G4Tubs("Rod", 0*mm, rodR, longRodLength/2., 0, twopi);
  G4LogicalVolume* shortRodLogical = new G4LogicalVolume(shortRodSolid, rodMaterial, "Rod");
  G4LogicalVolume* longRodLogical = new G4LogicalVolume(longRodSolid, rodMaterial, "Rod");

  // Set rod positions                                                                                                  
  G4double rodZPositions[6];
  for (G4int i = 0; i < 6; ++i) {
    rodZPositions[i] = (ringZPositions[i] + ringZPositions[i+1])/2.;
  }
  G4TwoVector rodXYPositions[3];
  for (G4int i = 0; i < 3; ++i) {
    G4double r = (ringRMin + ringRMax)/2.;
    G4double phi = i*120*deg;
    rodXYPositions[i].set(r*std::cos(phi), r*std::sin(phi));
  }

  // Place rods                                                                                                         
  for (G4int i = 0; i < 3; ++i) {
    G4ThreeVector position;
    // Set x and y                                                                                                      
    position.setX(rodXYPositions[i].x());
    position.setY(rodXYPositions[i].y());

    // Set z and place                                                                                                  
    position.setZ(rodZPositions[0]);
    new G4PVPlacement(nullptr, position,
                      shortRodLogical, "Rod", logicEnv,
                      false, 0, checkOverlaps);
    for (G4int k = 1; k < 5; ++k) {
      position.setZ(rodZPositions[k]);
      new G4PVPlacement(nullptr, position,
                        longRodLogical, "Rod", logicEnv,
                        false, 0, checkOverlaps);
    }
    position.setZ(rodZPositions[5]);
    new G4PVPlacement(nullptr, position,
                      shortRodLogical, "Rod", logicEnv,
                      false, 0, checkOverlaps);

  }

1 Like

Thanks a lot! This is much easier than what I implemented

Thanks, I will look into G4MultiUnion and try give it a try

I ended up using this code and now the simulation time has improved significantly. Thank you so much!