Geant4 Tips: Visualization

写在前面

笔记来源于官网文档Book for Application Developers中 visualization 一章,作适当整理和删减。

不同的图形驱动 (graphics driver) 的调用可能不同。如果读者参照的是最主流的下载教程,很有可能和笔者安装的一样的"OGL", 否则以下某些命令可能不适用。

另外对于相关内容已经有所了解的读者进行说明,在目录意指的范围内但是不在笔记包括范围内的部分:

  1. 自定义 Visualization Attributes
  2. 根据自定义Visualization Attributes进行着色、筛选,以及对应的FRED or HepRApp的使用
  3. 能够使用UI命令代替的代码实现

Visusalization

从UI command 可以理解可视化的过程。其中有三个概念

  1. scene
  2. scence handler
  3. viewer

scene 是一堆3D数据,viewer是最终渲染图片的工具,而scence handler的作用就是将scene这堆原始数据转换为viwer能够处理的数据形式。对于Geant4而言,也可以说一个图形驱动( graphics driver) 就是一些scence handler和viewer。

所以可视化的最简单的思路就是

空白数据
添加数据
处理数据
写入图形

对应了

create scence
add to scence
attach to scence handler
flush to viewer

当然,实际的UI command就是

/vis/open OGL // 打开graphics driver

/vis/scene/create  //第1步
/vis/scene/add/volume  // 2
/vis/sceneHandler/attach  // 3
/vis/viewer/refresh  // 4
/vis/viewer/flush  // 4 并声明渲染过程结束

一个最简单的 vis. mac也只需要以上6行代码。

以上是本文的核心内容,后文除Visuzalization attributies外,都是作为本节第2步 ”add to scene" 的补充和一些样式的调整。 Visualization attributes 需要额外说明是它和自定义HIt类的Draw方法是唯二两个使用代码来实现比UI命令更为方便的例子。

Visulization attributes

visualization attributes 通过代码来设置显然更为简便。

auto VisAtt = new G4VisAttributes();
VisAtt.SetColor(1.,0.,0.,1.);  // red, green, blue, alpha
VisAtt.SetVisibility(true);

除了volume之外,一些其他对象也可以设置visualization attributes.

G4Circle circle(pos);
G4Square square(pos);

circle.SetVisAttributes(vis);
square.SetVisAttributes(vis);

Volume & Field

考虑Volume和Field的最简单的语句为

/vis/scene/add/volume
/vis/scene/add/magneticField
/vis/scene/add/electricField 

而对于volume来说,还有特殊的UI命令

/vis/drawVolume

这个命令相当与将第1、2、3步骤整合起来相当于

/vis/scene/careate
/vis/scene/add/volume
/vis/cence/attach

如果没有参数,将会绘制 world 下的所有 volume, 如果带有world之外参数

/vis/drawVolume [physical-volume-name]

将会通过正则匹配所有符合要求的physical volume,以“Detector"为例,将会匹配到"Detector1", “Detector2”,…

drawVolume在参数化的volume渲染时需要处理大量的volume,降低运行效率,此时,可以设置

/vis/viewer/set/specialMeshRendering

提高运行效率

Trajectories

/vis/scene/add/trajectories

而trajectories是程序模拟过程中比较关心的作用过程,所有还有另外一些用法

  1. 额外参数
/vis/scene/add/trajectories [smooth/rich]

rich 会包含一些额外的信息,smooth只会保存和显示trajectory中的点。

  1. 累积

如果设置

/vis/scene/endOfEventAction accumulate [maxNumber]

新的Event会画在旧的Event上
如果

/vis/scene/endOfEventAction refresh

则会将旧的Event清除,再绘制新的Event

  1. 存储事件的刷新

如果每产生一个Trajectory就要立即渲染在输出中,大量的trajectory的处理的消耗较大。对于图形驱动(graphics driver) OGL 默认会在第100个Event结束的时候写入输出图形中,即

/vis/ogl/flushAt NthEvent 100

这种默认行为也可以修改,语法为

/vis/ogl/flushAt
<[endOfEvent|endOfRun|eachPrimitive|NthPrimitive|NthEvent|never]> <N>

或者更简单直接一点的方法,在run前不渲染,而run后再一齐处理

/vis/disable
/run/beamOn 10000
/vis/enable
/vis/reviewKeptEvents

Trajectory: 着色 (Style)

trajectory 的绘制样式会根据相应的 drawing model 来设定,通过以下命令来选择model

/vis/modeling/trajectories/select [model-name]

具体的model和相应的默认值可见此页

也可以使用命令查询

/vis/modeling/trajectories/list

也可以在原model的基础上自定义修改

/vis/modeling/trajectories/create/drawByCharge MyByCharge
/vis/modeling/trajectories/MyByCharge/set 0 white
/vis/modeling/trajectories/MyByCharge/set 1 green
/vis/modeling/trajectories/MyByCharge/set -1 red

drawByParticleId可以如以下修改

/vis/modeling/trajectories/create/drawByParticleID MyByParticle
/vis/modeling/trajectories/MyByParticle/set gamma green
/vis/modeling/trajectories/MyByParticle/set e- red
/vis/modeling/trajectories/MyByParticle/set proton yellow
/vis/modeling/trajectories/MyByParticle/set neutron white

此外,drawByAttributes是非常灵活的model,但是需要自定义相应的Visualization Attributes, 笔者暂时没有找到完整的相关案例,并且这个话题也是相对超出了笔者的需求,所以在这里不进行深入了。

Trajectory: 筛选 (Filtering)

具体的filter可见此页,处于笔记整理的完整性考虑,此处花费一些篇幅展示filtering的使用

# Create a particle filter. Configure to pass only gammas. Then
# invert to pass anything other than gammas. Set verbose printout,
# and then deactivate filter

/vis/filtering/trajectories/create/particleFilter
/vis/filtering/trajectories/particleFilter-0/add gamma
/vis/filtering/trajectories/particleFilter-0/invert true
/vis/filtering/trajectories/particleFilter-0/verbose true
/vis/filtering/trajectories/particleFilter-0/active false
# Create a charge filter. Configure to pass only neutral trajectories.
# Set verbose printout. Reset filter and reconfigure to pass only
# negatively charged trajectories.

/vis/filtering/trajectories/create/chargeFilter
/vis/filtering/trajectories/chargeFilter-0/add 0
/vis/filtering/trajectories/chargeFilter-0/verbose true
/vis/filtering/trajectories/chargeFilter-0/reset true
/vis/filtering/trajectories/chargeFilter-0/add -1

正如上一节,也有一种基于visualization Attributes进行筛选的实现方法,同样不在本文的范围之内。

另一种方法是通过/vis/reviewKeptEvents实现的,首先现在

可以在MyEventAction::EndOfEventAction中声明保留的Event

if ( some criterion ) {
  G4EventManager::GetEventManager()->KeepTheCurrentEvent();
}
// or
if ( some criterion ) {
  UImanager->ApplyCommand("/event/keepCurrentEvent");
}

然后在UI command使用

/vis/reviewKeptEvents

或者类似与draw/volume类似的

/vis/drawOnlyToBeKeptEvents

就可以只展示选择的Event的 trajectory.

Hits

需要在vis.mac中加入

/vis/scene/add/hits

并且需要在Hit类中设置可视化相关的信息,可以重写Hit类的draw方法

class MyHit:public G4VHit{
	public:
//...
    void Draw() override;
//...
}

void MyHit::Draw(){
G4Circle circle(Pos);
circle.SetVisAttributes(vis);
}

事实上关于Hit类重写实际操作比较复杂

HCofThisEvent
G4THitsCollection
G4VHit
G4THitsMap

G4THitsMap和G4THitsCollection是相互替代的关系,出现前者通常是由G4MultiFunctionalDetector创建的。

一个Event由许多HitCollection组成,每个具体的HitCollection需要通过对应的collectionID访问,而collectionID可以通过collecitonName或者HitCollection对象从G4SDManager中获得。

Allocator
collection ID
Hit
HitCollection
HCofThisEvent

重定义Hit类需要在Hit类中显式地定义对应的Allocator和HitCollection,并在SensitiveDetecoter中显式地创建对应的HitCollection,加入HCofTHisEvent中,并显示地创建Hit对象再将其加入对应的HitCollection中,以Example B2a为例:

在Hit的头文件中

// header
class TrackerHit : public G4VHit
{
  public:
    TrackerHit() = default;
    TrackerHit(const TrackerHit&) = default;
    ~TrackerHit() override = default;

    // operators
    TrackerHit& operator=(const TrackerHit&) = default;
    G4bool operator==(const TrackerHit&) const;

    inline void* operator new(size_t);
    inline void  operator delete(void*);

    // methods from base class
    void Draw() override;
    void Print() override;

    // Set methods
    void SetTrackID  (G4int track)      { fTrackID = track; };
    void SetPos      (G4ThreeVector xyz){ fPos = xyz; };

    // Get methods
    G4int GetTrackID() const     { return fTrackID; };
    G4ThreeVector GetPos() const { return fPos; };
    
  private:
    G4int         fTrackID = -1;
    G4ThreeVector fPos;
};

using TrackerHitsCollection = G4THitsCollection<TrackerHit>;
extern G4ThreadLocal G4Allocator<TrackerHit>* TrackerHitAllocator;

inline void* TrackerHit::operator new(size_t)
{
  if(!TrackerHitAllocator)
      TrackerHitAllocator = new G4Allocator<TrackerHit>;
  return (void *) TrackerHitAllocator->MallocSingle();
}

inline void TrackerHit::operator delete(void *hit)
{
  TrackerHitAllocator->FreeSingle((TrackerHit*) hit);
}

以上除了Set method 和Get method 全是必要的部分,也可以看作很好复用的模板。而Hit的主文件只有Draw、Print和==的实现,相对比较简单

G4ThreadLocal G4Allocator<TrackerHit>* TrackerHitAllocator = nullptr;

G4bool TrackerHit::operator==(const TrackerHit& right) const
{
  return ( this == &right ) ? true : false;
}
void TrackerHit::Draw()
{
// ...
}
void TrackerHit::Print()
{
// ...
}

创建HitCollection,创建Hit,将Hit放入HitCollection的过程在SensitiveDetector中完成。

// header
void   Initialize(G4HCofThisEvent* hitCollection) override;
void   EndOfEvent(G4HCofThisEvent* hitCollection) override;
TrackerHitsCollection* fHitsCollection = nullptr;

// implements
TrackerSD::TrackerSD(const G4String& name,
                     const G4String& hitsCollectionName)
 : G4VSensitiveDetector(name)
{
  collectionName.insert(hitsCollectionName);  // 与HitCollection创建相关
}

void TrackerSD::Initialize(G4HCofThisEvent* hce)
{
  // 创建HitCollection
  fHitsCollection
    = new TrackerHitsCollection(SensitiveDetectorName, collectionName[0]);

  // 通过ID 将HitCollection加入HCofThisEvent中
  G4int hcID
    = G4SDManager::GetSDMpointer()->GetCollectionID(collectionName[0]);
//  G4int hcID 
//    = G4SDManager::GetSDMpointer()->GetCollectionID(fHitsCollection);
  hce->AddHitsCollection( hcID, fHitsCollection );
}

G4bool TrackerSD::ProcessHits(G4Step* aStep,
                                     G4TouchableHistory*)
{
  // 创建 Hit
  auto newHit = new TrackerHit();
  newHit->SetTrackID  (aStep->GetTrack()->GetTrackID());
  newHit->SetPos (aStep->GetPostStepPoint()->GetPosition());
  // 将 Hit加入 HitCollection中
  fHitsCollection->insert( newHit );
  return true;
}

Culling & Cutting

culling 可以 不渲染特殊的一部分场景

# global
/vis/viewer/set/culling  global  [True/False]

# invisible
/vis/viewer/set/culling  invisible  [True/False]

/vis/viewer/set/culling  density  [True/False]  [[value]  [g/cm3][mg/cm3][kg/m3]]

/vis/viewer/set/culling  coveredDaughters  [True/False]

而 cutting 可以做一些切片

/vis/viewer/set/sectionPlane  on  2.0  0.0  0.0  cm  1.0  0.0  0.0

第一组向量是切片底面的任意点坐标,第二组向量是切面底面的法向量,其模对应切片的宽度。

可以同时做多个切片,这些切片可以取并集(默认), 也可以取交集

/vis/viewer/set/cutawayMode add  // default
/vis/viewer/set/cutawayMode multiply   // optional

或者清除切片

/vis/viewer/clearCutawayPlanes

总结

根据以上内容,我们可以写一个比较容易解读的vis.mac

/vis/open OGL 

/vis/scene/create  
/vis/scene/add/volume  
/vis/scene/add/hits
/vis/scene/add/trajectories smooth
/vis/scene/endOfEventAction accumulate 100

/vis/modeling/trajectories/create/drawByCharge
/vis/modeling/trajectories/select drawByCharge-0

/vis/filtering/trajectories/create/chargeFilter
/vis/filtering/trajectories/chargeFilter-0/add -1

/vis/sceneHandler/attach  
/vis/viewer/flush  

如果有需要,我们还可以调整trajectory处理的时机以优化渲染过程,略去一些不重要的volume或者在一些关键的部位作切片观察。

除了这些UI command, 我们还能够在代码中进行设置,自定义Hit类,调用Hit的Draw方法来实现Hits的可视化。

  • 21
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值