DLG4::VolumeBuilders
A fluent interface for Geant4 geometry definition.
Loading...
Searching...
No Matches
VolumeBuilder.hpp
Go to the documentation of this file.
1#pragma once
2#ifndef VOLUMEMAKER_HPP // for code analysis
3#define VOLUMEMAKER_HPP
4// Header implementation for VolumeMaker.hh
5// DO NOT INCLUDE THIS FILE DIRECTLY
6// INCLUDE VolumeBuilder.hh
7//
8// Created by Douglas S. Leonard on 6/8/25.
9// Implicit or explicit misrepresentation authorship is not protected under any license.
10// See surrounding files for licesne
11//
12#define STRINGIFY_HELPER(x) #x
13#define STRINGIFY(x) STRINGIFY_HELPER(x)
14#include <G4LogicalVolume.hh>
15#include <G4PVPlacement.hh>
16#include <G4SubtractionSolid.hh>
17#include "Linkable.hh"
19#include "VolumeBuilderTypes.hh"
21#include "Assembly.hh"
22#include <memory>
23
24// this will never be netered because we are ALWAYS entered FROM here:
25// but it helps code analysis
26#include <G4IntersectionSolid.hh>
27#include <G4ReflectedSolid.hh>
28
29#include "VolumeBuilder.hh"
30
31namespace DLG4::VolumeBuilders {
32 // used in CRTP implementation, should be improved probably.
33#define BASE VolumeBuilder<U>
34#define DERIVED typename BASE::DerivedPtr
35
36 //First: non-templated implementations
37
38 // These became trivial, but leaving them for future use.
39 inline void VolumeConfigs::copyFrom(const VolumeConfigs &other) {
40 //TODO: What's this about?
41 }
42
43 inline
47
49 copyFrom(other);
50 return *this;
51 }
52
53
54 // Now templates
55
56 // VolumeMaker methods //////////////////////////////////////////////////
57 template <typename U>
58 BASE::VolumeBuilder() : solid_ptr_(),
59 logicvol_ptr_(),
60 placement_() {
61 lv_configs_->material = G4Material::GetMaterial("VolumeBuilderDefaultGas");
62
63 if (!lv_configs_->material) {
64 // If lv_configs_->material is still nullptr (meaning it wasn't found),
65 // then create the new material and assign it.
66 lv_configs_->material = new G4Material(
67 // This is Geant. Of course we won't delete it :)
68 "VolumeBuilderDefaultGas", 2., 4. * CLHEP::g / CLHEP::mole,
69 0. * CLHEP::mg / CLHEP::cm3, kStateGas,
70 4.3 * CLHEP::kelvin, 1.e-8 * CLHEP::bar
71 );
72 }
73 // lv_configs_->material = new G4Material( // This is Geant. Of course we won't delete it :)
74 // "VolumeBuilderDefaultGas", 2., 4. * CLHEP::g / CLHEP::mole,
75 // 0. * CLHEP::mg / CLHEP::cm3, kStateGas,
76 // 4.3 * CLHEP::kelvin, 1.e-8 * CLHEP::bar);
77 // lv_configs_->vis_att = std::make_shared<G4VisAttributes>(true);
78 lv_configs_->vis_att = G4VisAttributes(true);
79 }
80
81 // copy ctor, called by derived copy ctor
82 template <typename U>
83 BASE::VolumeBuilder(const BASE &other): builder_configs_(other.builder_configs_),
84 boolean_configs_(other.boolean_configs_),
85 lv_configs_(other.lv_configs_),
86 placement_configs_(other.placement_configs_) {
87 // avoid masked bugs from stale view:
88 StoreBuilderView(nullptr);
89 StoreIStructurePtr(nullptr);
90 // don't copy products since they can only be linked once.
91 // Copy methods will do that.
92 }
93
94
95 //ctor to link to another builder
96 // If we put all our data in a common base class, we wouldn't need to
97 // template this. Is that better? Enh... Whatever.
98 template <typename U>
99 template <typename T, std::enable_if_t<std::is_base_of_v<IStructureBuilder, T>, int>>
101 const SharedPtr<T> &other,
102 SET_LINK_TYPE) : builder_configs_(other->builder_configs_,SET_LINK),
103 boolean_configs_(other->boolean_configs_,SET_LINK),
104 lv_configs_(other->lv_configs_, SET_LINK),
105 placement_configs_(other->placement_configs_,SET_LINK),
106 solid_ptr_(other->solid_ptr_,SET_LINK),
107 logicvol_ptr_(other->logicvol_ptr_,SET_LINK),
108 placement_(other->placement_,SET_LINK) {
109 builder_configs_->istructure_ptr = IStructurePtr(other);
110 }
111
112
113 template <typename U>
114 BASE::~VolumeBuilder() {
115 // optionally release resources (default)
116 // ReSharper disable once CppIfCanBeReplacedByConstexprIf
117 if (!has_ownership_) {
118 // This is SO wrong. This should be done at CREATION of members,
119 // not deletion of class.
120 // But it works for now and we're super restrictive on creation.
121 final_solid_ptr_.make_persistent();
122 solid_ptr_.make_persistent();
123 logicvol_ptr_.make_persistent();
124 placement_.make_persistent();
125 }
126 }
127
128 template <typename U>
129 DERIVED BASE::MakeLogicalVolume(G4Material *material, G4String name) {
130 ValidateForVolumeBuild(STRINGIFY(BASE) "MakeLogicalVolume");
131 //if no volume was previously built, should be safe to reset material.
132 if (material) {
133 lv_configs_->material = material;
134 }
135 if (name.empty()) {
136 name = this->GetLogicVolName() + "_L";
137 }
138 // Implicit construction through
139 // ctor args..
140 logicvol_ptr_.ConstructAndLink(final_solid_ptr_,
141 lv_configs_->material,
142 name);
144 ApplyAttributes_();
145 return this->shared_from_this();
146 }
147
148 template <typename U>
149 G4String BASE::GetLogicVolName() const {
150 if (!boolean_configs_->boolean_name.empty()) {
151 return boolean_configs_->boolean_name;
152 } else {
153 boolean_configs_->boolean_name = builder_configs_->name;
154 }
155 return boolean_configs_->boolean_name;
156 }
158 template <typename U>
159 DERIVED BASE::SetBooleanName(const G4String &name) {
160 this->boolean_configs_->boolean_name = name;
161 return this->shared_from_this();
162 }
163
164 template <typename U>
165 G4VSolid *BASE::GetBaseSolid() {
166 if (!this->solid_ptr_) {
167 MakeSolid();
168 }
169 return solid_ptr_.get_mutable();
170 }
171
172 template <typename U>
173 G4VPhysicalVolume *BASE::GetPlacement() {
174 if (!this->placement_) {
175 BASE::MakePlacement();
176 }
177 return this->placement_.get_mutable();
178 }
179
180 template <typename U>
181 G4LogicalVolume *BASE::GetLogicalVolume() {
182 if (!logicvol_ptr_) {
183 BASE::MakeLogicalVolume();
184 }
185 return logicvol_ptr_.get_mutable();
186 }
187
188 //The separation here was made to allow
189 //the non-fluent _impl calls to be used in ctors, but the new disable_shared_from_this
190 // solves that anyway. These can probably be consolidated now.
191 template <typename U>
192 DERIVED BASE::SetLogicalVolume(G4LogicalVolume *logical_volume) {
193 SetLogicalVolume_impl(logical_volume);
194 return this->shared_from_this();
195 }
196
197 //non-fluent implementation
198 template <typename U>
199 void BASE::SetLogicalVolume_impl(G4LogicalVolume *logical_volume) {
200 try {
201 this->logicvol_ptr_.LinkToRaw(logical_volume);
202 this->logicvol_ptr_.make_persistent();
203 } catch (const std::logic_error &) {
204 std::string error =
205 "Error in SetLogicalVolume()"
206 "for builder named: \"" + builder_configs_->name + "\""
207 " A Logical Volume has already been set or built\n"
208 "Start with a new builder or copy a configured one with "
209 "builder->SetPlacementBuilder(\"newname\")";
210 throw std::runtime_error(error);
211 }
212 }
213
214 //generic setsolid version
215 //FIXME: remove impl
216 template <typename U>
217 DERIVED BASE::SetSolid(G4VSolid *solid) {
218 SetSolid_impl(solid);
219 return this->shared_from_this();
220 }
221
222 // non-fluent implementation:
223 template <typename U>
224 void BASE::SetSolid_impl(G4VSolid *solid) {
225 try {
226 this->solid_ptr_.LinkToRaw(solid);
227 this->solid_ptr_.make_persistent();
228 } catch (const std::logic_error &) {
229 std::string error =
230 "Error in SetLogicalVolume();"
231 "for builder named: \"" + builder_configs_->name + "\"\n"
232 " A Logical Volume has already been set or built\n"
233 "Start with a new builder or copy a configured one with "
234 "builder->SetPlacementBuilder(\"newname\")";
235 throw std::runtime_error(error);
236 }
237 }
238
239
240 // End of Setsolid versions
241
242 template <typename U>
243 DERIVED BASE::AddUnion(const BuilderView &other, const Unit3Vec &new_offset,
244 G4RotationMatrix *rotation) {
245 bool is_subtraction = false;
246 bool is_intersection = false;
247 AddBoolean(other, is_subtraction, is_intersection, new_offset, rotation);
248 return this->shared_from_this();
249 }
250
251 template <typename U>
252 DERIVED BASE::AddSubtraction(const BuilderView &other, const Unit3Vec &new_offset,
253 G4RotationMatrix *rotation) {
254 if (other) {
255 bool is_subtraction = true;
256 bool is_intersection = false;
257 AddBoolean(other, is_subtraction, is_intersection, new_offset, rotation);
258 } else {
259 throw std::runtime_error("Error in AddBoolean(): "
260 "for builder named: \"" + builder_configs_->name + "\"\n"
261 "volume is invalid (null)");
262 }
263 return this->shared_from_this();
264 }
265
266 template <typename U>
267 DERIVED BASE::AddIntersection(const BuilderView &other, const Unit3Vec &new_offset,
268 G4RotationMatrix *rotation) {
269 if (other) {
270 bool is_subtraction = false;
271 bool is_intersection = true;
272 AddBoolean(other, is_subtraction, is_intersection, new_offset, rotation);
273 } else {
274 throw std::runtime_error("Error in AddBoolean() "
275 "for builder named: \"" + builder_configs_->name + "\"\n"
276 " volume is invalid (null)");
277 }
278 return this->shared_from_this();
279 }
280
281 template <typename U>
282 DERIVED BASE::AddBoolean(const BuilderView &other, bool is_subtraction,
283 bool is_intersection, const Unit3Vec &new_offset, G4RotationMatrix *rotation) {
284 auto offset = ProvisionUnits(new_offset);
285
286 if (other) {
287 boolean_configs_->booleans.emplace_back(
288 BooleanSolid{other, is_subtraction, is_intersection, offset, rotation});
289 } else {
290 throw std::runtime_error("Error in AddBoolean() "
291 "for builder named: \"" + builder_configs_->name + "\"\n"
292 " volume is invalid (null)");
293 }
294 return this->shared_from_this();
295 }
296
297 template <typename U>
298 G4VSolid * BASE::GetFinalSolid() {
299 if (!final_solid_ptr_ && placement_configs_->is_builder) {
300 MakeFinalSolid(std::string("")); // checks are done in there.
301 }
302 // should probably have a check here, although it's a bug anyway if not built now.
303 return this->final_solid_ptr_;
304 }
305
306
307 template <typename U>
308 DERIVED BASE::MakeFinalSolid(G4String boolean_name) {
309 this->ValidateForBooleanBuild(STRINGIFY(BASE) "MakeBooleans");
310 if (boolean_name.empty()) {
311 // may rename unimplemented
312 boolean_name = builder_configs_->name;
313 }
314 SetBooleanName(boolean_name);
315 G4String name_temp;
316 G4String final_name;
317 // if no booleans, we forward the original to the end result.
318 G4VSolid *temp_ptr = solid_ptr_.get_mutable();
319 if (boolean_configs_->boolean_name.empty()) {
320 final_name = builder_configs_->name + "_B";
321 } else {
322 final_name = boolean_configs_->boolean_name;
323 }
324 for (size_t i = 0; i < boolean_configs_->booleans.size(); i++) {
325 auto boolean = boolean_configs_->booleans[i];
326 const bool is_last = (i == (boolean_configs_->booleans.size() - 1)) && !boolean_configs_
327 ->reflect_z;
328 const size_t count = i + 1;
329 if (boolean_configs_->boolean_name.empty()) {
330 name_temp = builder_configs_->name + "_B" + std::to_string(count);
331 }
332 if (is_last) {
333 name_temp = final_name;
334 }
335 auto count_str = std::to_string(count); //union number string
336 if (!boolean.vol_ref->solid_ptr_) {
337 // lazy trigger solid construction:
338 boolean.vol_ref->MakeSolid();
339 }
340 if (boolean.is_subtraction) {
341 temp_ptr = new G4SubtractionSolid(
342 name_temp,
343 temp_ptr,
344 boolean.vol_ref->solid_ptr_.get_mutable(),
345 boolean.rotation,
346 boolean.offset
347 + ((boolean.rotation != nullptr)
348 ? *boolean.rotation * boolean.vol_ref->builder_configs_->
349 internal_offset
350 : 0 * boolean.offset)
351 - this->builder_configs_->internal_offset
352 );
353 } else if (boolean.is_intersection) {
354 temp_ptr = new G4IntersectionSolid(
355 name_temp,
356 temp_ptr,
357 boolean.vol_ref->solid_ptr_.get_mutable(),
358 boolean.rotation,
359 boolean.offset
360 + ((boolean.rotation != nullptr)
361 ? *boolean.rotation * boolean.vol_ref->builder_configs_->
362 internal_offset
363 : 0 * boolean.offset)
364 - this->builder_configs_->internal_offset
365 );
366 } else {
367 temp_ptr = new G4UnionSolid(
368 name_temp,
369 temp_ptr,
370 boolean.vol_ref->solid_ptr_.get_mutable(),
371 boolean.rotation,
372 boolean.offset
373 + ((boolean.rotation != nullptr)
374 ? *boolean.rotation * boolean.vol_ref->builder_configs_->
375 internal_offset
376 : 0 * boolean.offset)
377 - this->builder_configs_->internal_offset
378 );
379 }
380 }
381 if (boolean_configs_->reflect_z) {
382 temp_ptr = new G4ReflectedSolid(final_name,
383 temp_ptr,
384 G4ReflectZ3D(0));
385 auto io = this->builder_configs_->internal_offset;
386 // flip internal offset
387 this->builder_configs_->internal_offset = {io.getX(), io.getY(), -io.getZ()};
388 }
389
390 // we get one shot to link the final solid.
391 if (temp_ptr) {
392 final_solid_ptr_.LinkToRaw(temp_ptr);
393 } else {
394 throw std::runtime_error("Unknown Error in MakeBooleans "
395 "for builder named: \"" + builder_configs_->name + "\"\n"
396 "Failed to construct final solid.");
397 }
398
399 return this->shared_from_this();
400 }
401
402 template <typename U>
403 DERIVED BASE::SetName(const G4String &name) {
404 this->builder_configs_->name = name;
405 return this->shared_from_this();
406 }
407
408 template <typename U>
409 DERIVED BASE::SetMaterial(G4Material *material) {
410 lv_configs_->material = material;
411 return this->shared_from_this();
412 }
413
414 template <typename U>
415 DERIVED BASE::SetColor(const double r, const double g, const double b, const double alpha) {
416 lv_configs_->color = G4Colour(r, g, b, alpha);
417 ApplyAttributes_();
418 return this->shared_from_this();
419 }
420
421 template <typename U>
422 DERIVED BASE::SetAlpha(double alpha) {
423 lv_configs_->color.SetAlpha(alpha);
424 ApplyAttributes_();
425 return this->shared_from_this();
426 }
427
428 template <typename U>
429 DERIVED BASE::SetColor(const G4Colour &color) {
430 lv_configs_->color = color;
431 ApplyAttributes_();
432 return this->shared_from_this();
433 }
434
435 template <typename U>
436 DERIVED BASE::ForceSolid(bool x) {
437 lv_configs_->force_solid = x;
438 ApplyAttributes_();
439 return this->shared_from_this();
440 }
441
442 template <typename U>
443 DERIVED BASE::SetVisibility(bool x) {
444 lv_configs_->is_visible = x;
445 ApplyAttributes_();
446 return this->shared_from_this();
447 }
448
449
450 template <typename U>
451 // ReSharper disable once CppMemberFunctionMayBeConst
452 void BASE::ApplyAttributes_() {
453 if (logicvol_ptr_) {
454 lv_configs_->vis_att.SetVisibility(lv_configs_->is_visible);
455 lv_configs_->vis_att.SetColor(lv_configs_->color); // blue
456 lv_configs_->vis_att.SetForceSolid(lv_configs_->force_solid);
457 // logicvol_ptr_->SetVisAttributes(lv_configs_->vis_att.get());
458 logicvol_ptr_->SetVisAttributes(lv_configs_->vis_att);
459 } // will be applied when a logical volume is created.
460 }
461
462 template <typename U>
463 DERIVED BASE::MakePlacement() {
464 // the copy part comes from validate below, if we're already built:
465 ValidateForPVBuild(STRINGIFY(BASE) "MakePlacement");
466 // 1. Get the logical volume for *this* builder
467 auto *currentLogical = this->GetLogicalVolume();
468 if (!(explicit_physical_copy_name_set_
469 || explicit_copyno_set_
470 || placement_configs_->auto_copy_name
471 || placement_configs_->auto_copyno)
472 ) {
473 G4cout << "Warning: Placement Builder for " + builder_configs_->name +
474 " copied without setting name or copy number and auto naming and numbering have been disabled \n"
475 "Copy number auto-incrementing will be re-enabled \n"
476 " Use SetAutoCopyNo(true), SetAuCopyName(true), OverridePlacementName(\"name\"), SetCopyNo(num) \n"
477 " to pass a name or number to MakePlacement()" << G4endl;
478 placement_configs_->copy_no = PlacementNameRegistry::GetNameCount(
479 GetPlacementBaseName());
480 }
481
482 // 2. Resolve the Mother Logical Volume
483 G4LogicalVolume *effectiveMotherLogical = nullptr;
484
485 G4String base_name = this->GetPlacementBaseName();
486 G4String final_name;
487 if (placement_configs_->auto_copy_name) {
488 // auto naming incrementing
489 final_name = base_name + "_P" +
490 std::to_string(
491 PlacementNameRegistry::GetNameCount(GetPlacementBaseName()));
492 } else {
493 final_name = GetPlacementBaseName() + "_P";
495 // unless an override was set, then take it verbatim, user's blame :):
496 if (!placement_name_override_.empty()) {
497 final_name = placement_name_override_;
498 }
499
500 if (placement_configs_->mother == nullptr || !placement_configs_->mother->
501 GetLogicalVolume()) {
502 // will lazay trigger mother build if needed
503 G4cout << "WARNING in MakePlacement of " + final_name +
504 ". No mother volume was set or constructable.\n"
505 "Defaulting to world volume" << G4endl;
506 }
507
508 if (placement_configs_->mother) {
509 effectiveMotherLogical = placement_configs_->mother->GetLogicalVolume();
510 } else {
511 effectiveMotherLogical = nullptr; // world placement
512 }
513
514 auto transform = G4Transform3D(placement_configs_->total_rotation,
515 placement_configs_->total_translation);
516 // 4. Create the G4PVPlacement
517 // placement is <G4VPhysicalVolume> and can construct itself:
518 placement_.LinkToRaw(new G4PVPlacement(
519 transform, // G4Transform3D transform
520 currentLogical, // pCurrentLogical
521 final_name, // pName
522 effectiveMotherLogical, // pMotherLogical
523 false, // many option, only false allowed (unless you remember Geant3 :))
524 placement_configs_->copy_no, // pCopyNo
525 placement_configs_->surface_check // pSurfChk
526 ));
527 if (!placement_) {
528 throw std::runtime_error(
529 "MakeLogicalPlacement failed "
530 "for builder named: \"" + builder_configs_->name + "\"\n"
531 " pointer is null after calling G4PVPlacement().");
532 }
533 return this->shared_from_this();
534 }
535
536 // SetRotation
537 template <typename U>
538 DERIVED BASE::SetPhysRotation(const G4RotationMatrix &rot) {
539 G4RotationMatrix nullrot;
540 this->placement_configs_->rotation = rot;
541 PropagateTransform();
542 return this->shared_from_this();
543 }
544
545 template <typename U>
546 DERIVED BASE::StackPhysRotation(const G4RotationMatrix &rot) {
547 // some aliases
548 const auto &old_rot = placement_configs_->rotation;
549 const auto &old_trans = placement_configs_->translation;
550 // sanitize input
551 auto new_rot = rot * old_rot; // order matters.
552 auto new_trans = rot * old_trans;
553 this->placement_configs_->rotation = new_rot;
554 this->placement_configs_->translation = new_trans;
555 PropagateTransform();
556 return this->shared_from_this();
557 }
558
559 // SetTranslation
560 template <typename U>
561 DERIVED BASE::SetPhysOffset(const Unit3Vec &new_offset) {
562 const auto &offset = ProvisionUnits(new_offset);
563 placement_configs_->translation = offset;
564 PropagateTransform();
565 return this->shared_from_this();
566 }
567
568 // SetTranslation
569 template <typename U>
570 DERIVED BASE::StackPhysOffset(const Unit3Vec &stacked_offset) {
571 const auto &offset = ProvisionUnits(stacked_offset);
572 placement_configs_->translation += offset; // add translation
573 PropagateTransform();
574 return this->shared_from_this();
575 }
576
577 template <typename U>
578 DERIVED BASE::SetPhysTransform(const G4Transform3D &new_transform) {
579 SetPhysRotation(new_transform.getRotation()); // gets a copy on stack
580 SetPhysOffset({GetEffectiveDefaultUnit(), new_transform.getTranslation()});
581 PropagateTransform();
582 return this->shared_from_this();
583 }
584
585 template <typename U>
586 DERIVED BASE::StackPhysTransform(const G4Transform3D &new_transform) {
587 //order matters when stacking. Not when "setting"
588 StackPhysRotation(new_transform.getRotation()); // gets a copy on stack
589 StackPhysOffset({GetEffectiveDefaultUnit(), new_transform.getTranslation()});
590 PropagateTransform();
591 return this->shared_from_this();
592 }
593
594 template <typename U>
595 void BASE::PropagateTransform() {
596 // Apply transform of parent to relative transform of us, to get our total transform
597 G4RotationMatrix total_rotation = this->placement_configs_->parent_rotation
598 * this->placement_configs_->rotation;
599 G4ThreeVector total_translation = this->placement_configs_->parent_translation
600 + this->placement_configs_->parent_rotation
601 * (
602 this->placement_configs_->translation
603 + this->placement_configs_->rotation
604 * this->builder_configs_->internal_offset
605 );
606 if (!placement_configs_->is_builder) {
607 // we're an assembly, apply total transform as parent transform to our children
608 for (auto &child : placement_configs_->children) {
609 child->placement_configs_->parent_rotation = total_rotation;
610 child->placement_configs_->parent_translation = total_translation;
611 child->builder_configs_->builder_view->PropagateTransform();
612 }
613 } else {
614 // we're a concrete builder, so set our total transform
615 placement_configs_->total_rotation = total_rotation;
616 placement_configs_->total_translation = total_translation;
617 }
618 }
619
620 // OverridePlacementName
621 template <typename U>
622 DERIVED BASE::OverridePlacementName(const G4String &pName) {
623 explicit_physical_copy_name_set_ = static_cast<bool>(pName);
624 this->placement_name_override_ = pName;
625 return this->shared_from_this();
626 }
627
628 // SetCopyNo
629 template <typename U>
630 DERIVED BASE::SetCopyNo(G4int pCopyNo) {
631 explicit_copyno_set_ = static_cast<bool>(pCopyNo);
632 this->placement_configs_->copy_no = pCopyNo;
633 return this->shared_from_this();
634 }
636 // SetSurfaceCheck
637 template <typename U>
638 DERIVED BASE::SetSurfaceCheck(G4bool pSurfChk) {
639 this->placement_configs_->surface_check = pSurfChk;
640 return this->shared_from_this();
641 }
642
643 // SetMother (builder pointer)
644 template <typename U>
645 DERIVED BASE::SetMother(const BuilderView &mother) {
646 if (!mother) {
647 throw std::runtime_error("Error in VolumeBuilder::SetMother,"
648 "for builder named: \"" + builder_configs_->name + "\"\n"
649 " no valid mother physical volume provided");
650 // We cannot actually fully check this yet because we allow this to be a forward association
651 // The logical volume is not required to be constructable before we make a placement.
652 }
653 this->placement_configs_->mother = mother;
654 return this->shared_from_this();
655 }
656
657
658 template <typename U>
659 DERIVED BASE::ForkAndReset(const G4String &new_name) const {
660 NoNameCheck(new_name, "CopySolidBuilder");
661 DerivedPtr copy = this->Clone();
662 copy->SetName(new_name); // Set base name for derived class
663 copy->SetBooleanName("");
664 return copy;
665 }
666
667 template <typename U>
668 DERIVED BASE::ForkForFinalSolid(const G4String &new_name) {
669 NoNameCheck(new_name, "ForkForFinalSolid");
670 if (!solid_ptr_) {
671 MakeSolid();
672 }
673 DerivedPtr copy = this->Clone();
674 copy->SetName(new_name); // Set base name for derived class
675 copy->SetBooleanName("");
676 // all products are cleared by default, (re)build up to Solid
677 copy->solid_ptr_.Link(this->solid_ptr_);
678 return copy;
679 }
680
681 template <typename U>
682 DERIVED BASE::ForkForLogicalVolume(const G4String &new_name) {
683 NoNameCheck(new_name, "ForkForLogicalVolume");
684 if (!final_solid_ptr_ && placement_configs_->is_builder) {
685 [[maybe_unused]] auto discard = GetFinalSolid();
686 }
687 DerivedPtr copy = this->Clone();
688 copy->SetName(new_name); // Set base name for derived class
689 copy->SetBooleanName("");
690 // all products are cleared by default, (re)build up to Solid
691 if (solid_ptr_ && placement_configs_->is_builder) {
692 copy->solid_ptr_.Link(this->solid_ptr_);
693 }
694 if (final_solid_ptr_ && placement_configs_->is_builder) {
695 copy->final_solid_ptr_.Link(this->final_solid_ptr_);
696 }
697 return copy;
698 }
699
700 template <typename U>
701 DERIVED BASE::ForkForPlacement(std::optional<int> copy_no,
702 const G4String &name_override, bool parent_name_was_set) {
703 // really a clone of logical volume:
704 if (!logicvol_ptr_ && placement_configs_->is_builder) {
705 MakeLogicalVolume();
706 }
707 DerivedPtr copy = this->Clone();
708 copy->explicit_copyno_set_ = static_cast<bool>(copy_no);
709 copy->explicit_physical_copy_name_set_ = !name_override.empty() || parent_name_was_set;
710 copy->placement_name_override_ = name_override;
711 // register name and get provisional copy_no:
712 copy->placement_configs_->copy_no = PlacementNameRegistry::IncrementNameCount(
713 GetPlacementBaseName());
714 //Override if copy_no was passed:
715 copy->placement_configs_->copy_no = copy_no.value_or(copy->placement_configs_->copy_no);
716
717 // Restore logical_volume pointer and below
718 copy->solid_ptr_.Link(this->solid_ptr_);
719 copy->final_solid_ptr_.Link(this->final_solid_ptr_);
720 copy->logicvol_ptr_.Link(this->logicvol_ptr_);
721
722 return copy;
723 }
724
725
726 template <typename U>
727 DERIVED BASE::SetAutoPlacementNaming(const bool set) {
728 placement_configs_->auto_copy_name = set;
729 if (set) {
730 placement_configs_->auto_copyno = false;
731 }
732 return this->shared_from_this();
733 }
734
735 template <typename U>
736 DERIVED BASE::SetAutoCopyNo(const bool set) {
737 placement_configs_->auto_copyno = set;
738 if (set) {
739 placement_configs_->auto_copy_name = false;
740 }
741 return this->shared_from_this();
742 }
743
744
745 // 3. Determine the Placement Name
746 template <typename U>
747 G4String BASE::GetPlacementBaseName() const {
748 // top priority is actually boolean name. override name is not supposed to get
749 // extended, but we'll fallback to it if it's all there is and if auto name incrementing ends up used.
750 G4String base_name = boolean_configs_->boolean_name;
751 if (base_name.empty()) {
752 if (builder_configs_->name.empty()) {
753 if (placement_name_override_.empty()) {
754 throw std::runtime_error("Err in DLG4::VolumeBuilders::"
755 "for builder named: \"" + builder_configs_->name +
756 "\"\n"
757 "GetPlacementBaseName\n"
758 "No names are defined. Cannot build");
759 } else {
760 base_name = placement_name_override_;
761 }
762 } else {
763 base_name = builder_configs_->name;
764 }
765 }
766 // prepend parent name, if any, for hierarchies
767 if (!placement_configs_->parent_name.empty()) {
768 base_name = placement_configs_->parent_name + ":" + base_name;
769 }
770 return base_name;
771 }
772
773
774 template <typename U>
775 DERIVED BASE::CopyPlacementConfigsFrom(const BuilderView &other) {
776 this->placement_configs_ = other->placement_configs_;
777 // receiver is responsible for unique naming/numbering.
778 // we can't clobber auto-name/number incrementing from another config:
779 // Does not copy the solid/boolean, just to get configs ONLY
780 return this->shared_from_this();
781 }
782
783 template <typename U>
784 DERIVED BASE::CopyVolumeConfigsFrom(const BuilderView &other) {
785 // Booleans are NOT considered as part of Volume Configs.
786 // We keep our booleans.
787 auto name = this->builder_configs_->name; // backup our name
788 auto booleans = this->boolean_configs_->booleans;
789 this->lv_configs_ = other->lv_configs_;
790 // restor name. This is easier than keeping name separately. :P
791 this->builder_configs_->name = name;
792 this->boolean_configs_->booleans = booleans; // restore ours.
793 return this->shared_from_this();
794 }
795
796
797 template <typename U>
798 void BASE::make_persistent(const std::shared_ptr<void> &obj) {
799 std::lock_guard<std::mutex> lock(s_registry_mutex);
800 black_hole.push_back(obj);
801 }
802
803 template <typename U>
804 void BASE::ValidateSolidNotBuilt(const std::string &operation) const {
805 if (solid_ptr_) {
806 throw std::runtime_error("Cannot " + operation + " - solid already built "
807 "for builder named: \"" + builder_configs_->name + "\"\n"
808 "!");
809 }
810 }
811
812 template <typename U>
813 void BASE::ValidateBooleanNotBuilt(const std::string &operation) const {
814 if (final_solid_ptr_) {
815 throw std::runtime_error("Cannot " + operation + " - boolean already built!"
816 "for builder named: \"" + builder_configs_->name + "\"\n"
817 );
818 }
819 }
820
821 template <typename U>
822 void BASE::ValidateLogicalNotBuilt(const std::string &operation) const {
823 if (logicvol_ptr_) {
824 throw std::runtime_error("Cannot " + operation + " - logical volume already built!"
825 "for builder named: \"" + builder_configs_->name + "\"\n"
826 );
827 }
828 }
829
830 template <typename U>
831 void BASE::ValidatePlacementNotBuilt(const std::string &operation) const {
832 if (placement_) {
833 throw std::runtime_error("Cannot " + operation + " - placement already built!"
834 "for builder named: \"" + builder_configs_->name + "\"\n"
835 );
836 }
837 }
838
841 //Validator for physical volume build:
842 template <typename U>
843 void BASE::ValidateForPVBuild(std::string const &site) {
844 if (placement_) {
845 throw std::runtime_error(">>> Error in " + site + " Physical Volume was already built\n"
846 "for builder named: \"" + builder_configs_->name + "\"\n"
847 "Use ForkForPlacement to copy and rebuild.");
848 }
849 // ReSharper disable once CppRedundantBooleanExpressionArgument
850 if (!logicvol_ptr_ && enable_full_lazy_builds && placement_configs_->is_builder) {
851 MakeLogicalVolume(); // will trigger ValidateForVolumeBuilder below
852 }
853 if (!logicvol_ptr_ && placement_configs_->is_builder) {
854 throw std::runtime_error(
855 "Error in " + site + ": LogicVolume is null after MakeLogicalVolume()."
856 "for builder named: \"" + builder_configs_->name + "\"\n"
857 );
858 }
859 }
860
861 template <typename U>
862 void BASE::ValidateForVolumeBuild(std::string const &site) {
863 if (logicvol_ptr_) {
864 std::string error = "Error in " + site + " Booleans were already built\n"
865 "for builder named: \"" + builder_configs_->name + "\"\n"
866 "You can copy and rename the builder to reset it and build again.\n";
867 throw std::runtime_error(error);
868 }
869 // ReSharper disable once CppRedundantBooleanExpressionArgument
870 if (!final_solid_ptr_ && enable_full_lazy_builds && placement_configs_->is_builder) {
871 MakeFinalSolid(); // Will trigger ValidateForBooleanBuild below
872 }
873 if (!final_solid_ptr_ && placement_configs_->is_builder) {
874 // MakeBooleans always set it.
875 throw std::runtime_error(
876 "Error in ValidateForVolumeBuild from " + site +
877 "for builder named: \"" + builder_configs_->name + "\"\n"
878 ": It's not possible to produce this error.");
879 }
880 }
881
882 template <typename U>
883 void BASE::ValidateForBooleanBuild(std::string const &site) {
884 if (final_solid_ptr_) {
885 std::string error = "Error in " + site + " A solid was already built\n"
886 "for builder named: \"" + builder_configs_->name + "\"\n"
887 "You can copy and rename the builder to reset it and build again.";
888 throw std::runtime_error(error);
889 }
890 // ReSharper disable once CppRedundantBooleanExpressionArgument
891 if (!solid_ptr_ && enable_full_lazy_builds && placement_configs_->is_builder) {
892 MakeSolid(); // Trigger solid creation if not already done
893 }
894 if (!solid_ptr_ && placement_configs_->is_builder) {
895 throw std::runtime_error("Error in " + site + ": "
896 "for builder named: \"" + builder_configs_->name + "\"\n"
897 "Solid is null after MakeSolid().");
898 }
899 }
900
901 template <typename U>
902 DERIVED BASE::SetDefaultUnit(G4double unit) {
903 this->builder_configs_->default_unit = unit;
904 return this->shared_from_this();
905 }
906
907 template <typename U>
908 G4double BASE::GetEffectiveDefaultUnit() const {
909 auto temp = builder_configs_.get();
910 auto local = temp->default_unit;
911 // auto local = builder_configs_->default_unit;
913 G4double default_unit = local.value_or(global);
914 return default_unit;
915 }
916
917 template <typename U>
918 DERIVED BASE::ReflectZFinalSolid() {
919 if (final_solid_ptr_) {
920 throw std::runtime_error("Error VolumeBuilder::ReflectZFinalSolid, \n"
921 "The final solid is already built. \n");
922 }
923 boolean_configs_->reflect_z = true;
924 return this->shared_from_this();
925 }
926
927 template <typename U>
928 DERIVED BASE::ReflectZBaseSolid() {
929 if (final_solid_ptr_) {
930 throw std::runtime_error("Error VolumeBuilder::ReflectZBaseSolid, \n"
931 "The base solid is already built. \n");
932 }
933 builder_configs_->reflect_base_solid_z = true;
934 return this->shared_from_this();
935 }
936
937 template <typename U>
938 DERIVED BASE::MakeSolid() {
939 G4VSolid *solid;
940 G4String final_name = GetBuilderName();
941 if (builder_configs_->reflect_base_solid_z) {
942 solid = SolidConstructor(final_name + "_proto_solid");
943 solid = new G4ReflectedSolid(final_name, solid, G4ReflectZ3D(0));
944 } else {
945 solid = SolidConstructor(final_name);
946 }
947 solid_ptr_.LinkToRaw(solid);
948 return this->shared_from_this();
949 }
950
951
952 // apply the active default unit to a provided vector
953 // unless the vector alerady has units.
954 template <typename U>
955 G4ThreeVector BASE::ProvisionUnits(const Unit3Vec &vec) const {
956 // will apply default or internal unit.
957 return vec.apply_units(GetEffectiveDefaultUnit());
958 }
959
960 template <typename U>
961 void BASE::StoreIStructurePtr(const IStructurePtr &istructure_ptr) {
962 // can only set this ONCE!!
963 builder_configs_->istructure_ptr = IStructurePtr(istructure_ptr);
964 }
965
966 template <typename U>
967 void BASE::StoreBuilderView(const BuilderView &builder_view) {
968 // can only set this ONCE!!
969 builder_configs_->builder_view = builder_view;
970 }
971
972 template <typename U>
973 G4String BASE::GetBuilderName() const {
974 return this->builder_configs_->name;
975 }
976
977 template <typename U>
978 BuilderView BASE::ToBuilderView() const {
979 // calls the BuilderView copy/convert ctor::
980 // presently the i_shared converter only works with l-value.
981 std::shared_ptr<U> builder_std_ptr =
982 std::const_pointer_cast<U>(this->shared_from_this());
983 auto x = DerivedPtr(builder_std_ptr);
984 return BuilderView(x);
985 }
986
987 template <typename U>
988 StructureView BASE::ToStructureView() const {
989 // calls the structure view copy/convert ctor:
990 std::shared_ptr<U> builder_std_ptr =
991 std::const_pointer_cast<U>(this->shared_from_this());
992 auto x = DerivedPtr(builder_std_ptr);
993 return StructureView(x);
994 }
995
996 template <typename U>
997 SharedPtr<IStructureBuilder> BASE::clone_impl() const {
998 const U &derived_ref = static_cast<const U &>(*this); // downcast
999 auto retval = new U(derived_ref); // copy
1000 auto shared_ptr = i_shared_ptr<U>(retval);
1001 return shared_mutable_this(retval); // wrap and return.
1002 }
1003
1004 template <typename U>
1005 DERIVED BASE::AddTo(BuilderViewList &list) const {
1006 list.emplace_back(this->ToBuilderView());
1007 auto retval = shared_mutable_this(this);
1008 return retval;
1009 }
1010
1011 template <typename U>
1012 DERIVED BASE::AddTo(StructureViewList &list) const {
1013 list.emplace_back(this->ToStructureView());
1014 auto retval = shared_mutable_this(this);
1015 return retval;
1016 }
1017
1018 template <typename U>
1019 DERIVED BASE::AddTo(AssemblyPtr &assembly) const {
1020 assembly->AddStructure(this->ToStructureView());
1021 auto retval = shared_mutable_this(this);
1022 return retval;
1023 }
1024
1025#undef DERIVED
1026#undef BASE
1027} // namespace DLG4::VolumeBuilders
1028
1029#endif //VOLUMEMAKER_HPP
1030//TODO Check release build for -g omission.
#define SET_LINK
Definition Linkable.hh:63
#define SET_LINK_TYPE
Definition Linkable.hh:62
#define DERIVED
#define STRINGIFY(x)
#define BASE
A 3D vector that carries its own unit information.
G4ThreeVector apply_units(const double dflt_unit) const
A wrapper for std::shared_ptr that allows and facilitates many implicit(i) type conversions.
std::shared_ptr< T > shared_mutable_this(const std::enable_shared_from_this< T > *obj)
SharedPtr< VolumeBuilderReference > BuilderView
Common interface for all volume builder types.
SharedPtr< IStructureBuilder > IStructurePtr
True polymorphic class base view for all structures Mostly for internal use.
std::vector< StructureView > StructureViewList
a user type to hold many structures
std::vector< BuilderView > BuilderViewList
a user type to hold many builders
SharedPtr< StructureBuilderReference > StructureView
Common interface for all structure types.
std::vector< std::shared_ptr< void > > black_hole
Definition Linkable.hh:69
std::mutex s_registry_mutex
Definition Linkable.hh:70
void copyFrom(const VolumeConfigs &other)
VolumeConfigs & operator=(const VolumeConfigs &other)
Operator =.