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