48 Commits

Author SHA1 Message Date
AndreaRigoni
bbd7493d9f add QCanvas Root and viewport pane in gcompose 2026-03-21 15:41:58 +00:00
AndreaRigoni
033fb598c7 add detector simulation 2026-03-20 00:16:55 +00:00
AndreaRigoni
c44a7738c0 make scene able to run parallel simulations 2026-03-19 21:51:38 +00:00
AndreaRigoni
85e1f1448f feat: Implement new MIP Rainbow and Additive rendering presets and add interactive test controls. 2026-03-19 16:44:00 +00:00
AndreaRigoni
6234dffaa7 add voximage represetation and test 2026-03-19 16:39:05 +00:00
AndreaRigoni
80952cc706 add cylinder emitter 2026-03-19 16:19:36 +00:00
AndreaRigoni
ae27e9d46d add cylinder 2026-03-19 16:03:57 +00:00
AndreaRigoni
a8a313e5cf added skyplaneEmitter 2026-03-19 15:45:48 +00:00
AndreaRigoni
7c8c7beae4 add ecomug 2026-03-19 14:57:26 +00:00
AndreaRigoni
a8c0d5edc2 handler toggle 1,2,3 keys 2026-03-19 14:28:20 +00:00
AndreaRigoni
dbb5f24933 fix zoom to selected 2026-03-19 14:18:31 +00:00
AndreaRigoni
1e6e3ae4f4 emitter representation 2026-03-19 13:57:10 +00:00
AndreaRigoni
ca5f576b99 add triangle mesh affine transform 2026-03-19 13:11:49 +00:00
AndreaRigoni
4cb4560921 qadmesh affine transform parenting 2026-03-19 12:54:31 +00:00
AndreaRigoni
887b3b36f0 add lambda connection 2026-03-19 12:51:37 +00:00
AndreaRigoni
2f163a762c make test fix test wiht correct units 2026-03-19 10:42:14 +00:00
AndreaRigoni
12657167f1 add detector chamber projection plane representation in vtk 2026-03-19 10:21:01 +00:00
AndreaRigoni
c265adadfc fix transform adding pre-translation 2026-03-19 10:20:33 +00:00
AndreaRigoni
c8eec163a6 projection plane on local coordinates 2026-03-19 09:52:19 +00:00
AndreaRigoni
ca2223e04c detector chamber 2026-03-19 09:46:36 +00:00
AndreaRigoni
176a82f108 add zoom to selected 2026-03-18 23:35:51 +00:00
AndreaRigoni
3b02bb26ac refactor: Simplify vtkDetectorChamber by removing redundant transform management and improve vtkHandlerWidget rotation calculation using ray-plane intersection. 2026-03-18 23:08:22 +00:00
AndreaRigoni
a9b66a4e12 add grid annotation 2026-03-18 21:44:29 +00:00
AndreaRigoni
cca29ef837 fixed vtk containerbox handler 2026-03-18 21:32:33 +00:00
AndreaRigoni
0e8ac47fcf grid axis 2026-03-18 07:23:58 +00:00
AndreaRigoni
92a06f6274 activate grid button and widget size 2026-03-17 22:56:56 +00:00
AndreaRigoni
553bb7fd61 widget in viewport 2026-03-17 17:28:26 +00:00
AndreaRigoni
bc437a3913 fix segfault 2026-03-17 15:47:27 +00:00
AndreaRigoni
e6e0bccffb split viewport for Qt and VtkViewer 2026-03-17 15:29:07 +00:00
AndreaRigoni
4569407d18 add vtkHandlerWidget 2026-03-17 11:13:35 +00:00
AndreaRigoni
d8ef413216 vtkGeantEvent 2026-03-16 17:51:53 +00:00
AndreaRigoni
c63a1ae047 geant events for multiple scattering 2026-03-14 23:33:31 +00:00
AndreaRigoni
692cdf7ae3 add Geant namespace 2026-03-14 14:01:44 +00:00
AndreaRigoni
e5dfb75262 add Normals to meshes 2026-03-14 13:23:28 +00:00
AndreaRigoni
35e4fb949d fix tests 2026-03-14 12:28:40 +00:00
AndreaRigoni
20d4967356 add quadmesh 2026-03-14 10:28:16 +00:00
AndreaRigoni
6bf9eaf309 add Qt viewport 2026-03-13 22:36:52 +00:00
AndreaRigoni
a142c5d060 add units 2026-03-13 21:40:14 +00:00
AndreaRigoni
61052f80bc add geant4 scene and gcompose app 2026-03-13 17:19:51 +00:00
AndreaRigoni
f2133c31d5 DetectorChamber vtk handler 2026-03-10 08:18:17 +00:00
AndreaRigoni
00275ac56d vtk camera position widget on viewer 2026-03-08 16:51:39 +00:00
AndreaRigoni
1374821344 added gizmo but not yet working 2026-03-08 10:21:38 +00:00
AndreaRigoni
2548582036 attach a widget (not working well yet) 2026-03-08 09:42:28 +00:00
AndreaRigoni
32a1104769 detector chamber in vtk 2026-03-08 08:46:21 +00:00
AndreaRigoni
3be7ec2274 add stl test 2026-03-08 08:05:22 +00:00
AndreaRigoni
38dd416ced vix raytracer representation 2026-03-07 09:07:07 +00:00
AndreaRigoni
e8f8e96521 reorganization of sources, moving cmt pertaining structures into HEP folder 2026-03-07 08:58:31 +00:00
AndreaRigoni
49cf0aeedd feat: disable camera spin/inertia by introducing a custom interactor style.r 2026-03-06 17:31:29 +00:00
177 changed files with 12225 additions and 2937 deletions

View File

@@ -34,7 +34,9 @@
#cmakedefine HAVE_FLOOR
/* Having Geant4 installed */
#ifndef HAVE_GEANT4
#cmakedefine HAVE_GEANT4
#endif
/* Define to 1 if you have the <inttypes.h> header file. */
#cmakedefine HAVE_INTTYPES_H

View File

@@ -3,7 +3,14 @@
##### CMAKE LISTS ##############################################################
################################################################################
if(EXISTS "${CMAKE_BINARY_DIR}/conan_toolchain.cmake")
include("${CMAKE_BINARY_DIR}/conan_toolchain.cmake")
endif()
cmake_minimum_required (VERSION 3.26)
set(QT_NO_VERSION_CHECK TRUE)
if(POLICY CMP0167)
cmake_policy(SET CMP0167 NEW)
endif()
@@ -107,6 +114,8 @@ set(Boost_USE_MULTITHREADED ON)
set(Boost_USE_STATIC_RUNTIME OFF)
message(STATUS "CMAKE_PREFIX_PATH is ${CMAKE_PREFIX_PATH}")
find_package(HDF5 REQUIRED CONFIG)
find_package(Boost 1.45.0 COMPONENTS program_options serialization unit_test_framework REQUIRED)
include_directories(${Boost_INCLUDE_DIRS})
@@ -118,15 +127,12 @@ find_package(ROOT CONFIG REQUIRED)
include(${ROOT_USE_FILE})
find_package(VTK REQUIRED)
# include(${VTK_USE_FILE})
find_package(pybind11 REQUIRED)
option(CENTOS_SUPPORT "VTK definitions for CentOS" OFF)
if(CENTOS_SUPPORT)
find_package(VTK CONFIG REQUIRED)
include(${VTK_USE_FILE})
# include(${VTK_USE_FILE})
else()
find_package(VTK REQUIRED
COMPONENTS CommonColor
@@ -146,7 +152,36 @@ else()
RenderingFreeType
RenderingGL2PSOpenGL2
RenderingOpenGL2
RenderingVolumeOpenGL2)
RenderingVolumeOpenGL2
IOGeometry
GUISupportQt)
endif()
find_package(Qt6 COMPONENTS Widgets)
if(Qt6_FOUND)
add_compile_definitions(HAVE_QT)
endif()
find_package(Geant4)
if(Geant4_FOUND)
message(STATUS "Geant4 libs: ${Geant4_LIBRARIES}")
add_compile_definitions(HAVE_GEANT4)
set(HAVE_GEANT4 1)
# Sanitize Geant4 targets to remove Qt5 dependencies that conflict with VTK/Qt6
if(TARGET Geant4::G4interfaces)
set_target_properties(Geant4::G4interfaces PROPERTIES
INTERFACE_LINK_LIBRARIES "Geant4::G4global;Geant4::G4graphics_reps;Geant4::G4intercoms"
)
endif()
if(TARGET Geant4::G4OpenGL)
set_target_properties(Geant4::G4OpenGL PROPERTIES
INTERFACE_LINK_LIBRARIES "Geant4::G4vis_management;Geant4::G4graphics_reps;Geant4::G4geometry;Geant4::G4materials;Geant4::G4intercoms;Geant4::G4global;OpenGL::GL;OpenGL::GLU"
)
endif()
else()
message(STATUS "Geant4 NOT found - optional features will be disabled")
set(HAVE_GEANT4 0)
endif()
set(CMAKE_REQUIRED_INCLUDES CMAKE_REQUIRED_INCLUDES math.h)
@@ -204,8 +239,8 @@ add_subdirectory(${SRC_DIR}/Core)
include_directories(${SRC_DIR}/Math)
add_subdirectory(${SRC_DIR}/Math)
include_directories(${SRC_DIR}/Detectors)
add_subdirectory(${SRC_DIR}/Detectors)
include_directories(${SRC_DIR}/HEP)
add_subdirectory(${SRC_DIR}/HEP)
include_directories(${SRC_DIR}/Root)
add_subdirectory(${SRC_DIR}/Root)
@@ -215,7 +250,7 @@ add_subdirectory(${SRC_DIR}/Vtk)
add_subdirectory(${SRC_DIR}/Python)
#add_subdirectory("${SRC_DIR}/utils/make_recipe")
add_subdirectory(app)
## Documentation and packages

3
app/CMakeLists.txt Normal file
View File

@@ -0,0 +1,3 @@
if(HAVE_GEANT4)
add_subdirectory(gcompose)
endif()

View File

@@ -0,0 +1,43 @@
add_executable(gcompose
src/main.cpp
src/MainWindow.h
src/MainWindow.cpp
src/QViewportPane.h
src/QViewportPane.cpp
src/MainPanel.h
src/MainPanel.cpp
)
set_target_properties(gcompose PROPERTIES
AUTOMOC ON
AUTOUIC ON
AUTORCC ON
)
target_include_directories(gcompose PRIVATE
${SRC_DIR}
${PROJECT_BINARY_DIR}
${Geant4_INCLUDE_DIRS}
${VTK_INCLUDE_DIRS}
)
# Filter Geant4 libraries to remove Qt-dependent ones
set(Geant4_LIBS_FILTERED ${Geant4_LIBRARIES})
if(Geant4_LIBS_FILTERED)
list(REMOVE_ITEM Geant4_LIBS_FILTERED Geant4::G4interfaces Geant4::G4OpenGL Geant4::G4visQt3D)
endif()
target_link_libraries(gcompose
mutomCore
mutomMath
mutomGeant
mutomVtk
mutomRoot
${Geant4_LIBS_FILTERED}
${VTK_LIBRARIES}
Qt6::Widgets
VTK::GUISupportQt
)
install(TARGETS gcompose RUNTIME DESTINATION bin)

View File

@@ -0,0 +1,49 @@
#include "MainPanel.h"
#include "QViewportPane.h"
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QSplitter>
#include <QLabel>
#include <QPushButton>
MainPanel::MainPanel(QWidget* parent) : QWidget(parent) {
auto* mainLayout = new QVBoxLayout(this);
mainLayout->setContentsMargins(0, 0, 0, 0);
mainLayout->setSpacing(0);
// 1. Top Menu Panel
auto* menuPanel = new QWidget(this);
menuPanel->setFixedHeight(36);
menuPanel->setStyleSheet("background-color: #2b2b2b; border-bottom: 1px solid #111;");
auto* menuLayout = new QHBoxLayout(menuPanel);
menuLayout->setContentsMargins(10, 0, 10, 0);
menuLayout->setSpacing(15);
auto* logo = new QLabel("G-COMPOSE", menuPanel);
logo->setStyleSheet("font-weight: bold; color: #0078d7; font-size: 14px; letter-spacing: 1px;");
auto* btnOpen = new QPushButton("Open", menuPanel);
auto* btnSave = new QPushButton("Save", menuPanel);
QString btnStyle = "QPushButton { background: transparent; color: #ccc; border: none; padding: 5px 10px; }"
"QPushButton:hover { background: #3c3c3c; color: white; border-radius: 4px; }";
btnOpen->setStyleSheet(btnStyle);
btnSave->setStyleSheet(btnStyle);
menuLayout->addWidget(logo);
menuLayout->addWidget(btnOpen);
menuLayout->addWidget(btnSave);
menuLayout->addStretch();
mainLayout->addWidget(menuPanel);
// 2. Central Splitter Area
m_rootSplitter = new QSplitter(Qt::Horizontal, this);
m_firstPane = new QViewportPane(m_rootSplitter);
m_rootSplitter->addWidget(m_firstPane);
mainLayout->addWidget(m_rootSplitter, 1);
}
MainPanel::~MainPanel() {}

View File

@@ -0,0 +1,22 @@
#ifndef MAINPANEL_H
#define MAINPANEL_H
#include <QWidget>
class QSplitter;
class QViewportPane;
class MainPanel : public QWidget {
Q_OBJECT
public:
explicit MainPanel(QWidget* parent = nullptr);
virtual ~MainPanel();
QViewportPane* getFirstPane() const { return m_firstPane; }
private:
QSplitter* m_rootSplitter;
QViewportPane* m_firstPane;
};
#endif // MAINPANEL_H

View File

@@ -0,0 +1,16 @@
#include "MainWindow.h"
#include <QSplitter>
#include "MainPanel.h"
using namespace uLib;
MainWindow::MainWindow(QWidget* parent) : QMainWindow(parent) {
m_panel = new MainPanel(this);
setCentralWidget(m_panel);
setWindowTitle("gcompose - Qt VTK Interface");
resize(1200, 800);
}
MainWindow::~MainWindow() {
}

View File

@@ -0,0 +1,26 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QVTKOpenGLNativeWidget.h>
class MainPanel;
namespace uLib {
namespace Vtk {
}
}
class MainWindow : public QMainWindow {
Q_OBJECT
public:
MainWindow(QWidget* parent = nullptr);
virtual ~MainWindow();
MainPanel* getPanel() { return m_panel; }
private:
MainPanel* m_panel;
};
#endif

View File

@@ -0,0 +1,169 @@
#include "QViewportPane.h"
#include <Vtk/vtkQViewport.h>
#include <Root/QCanvas.h>
#include <QVBoxLayout>
#include <QHBoxLayout>
#include <QLabel>
#include <QToolButton>
#include <QMenu>
#include <QAction>
#include <QSplitter>
#include <vtkCamera.h>
QViewportPane::QViewportPane(QWidget* parent) : QWidget(parent), m_viewport(nullptr) {
m_layout = new QVBoxLayout(this);
m_layout->setContentsMargins(0, 0, 0, 0);
m_layout->setSpacing(0);
// Title bar setup
m_titleBar = new QWidget(this);
m_titleBar->setFixedHeight(22);
m_titleBar->setStyleSheet("background-color: #333; color: white;");
auto* titleLayout = new QHBoxLayout(m_titleBar);
titleLayout->setContentsMargins(5, 0, 5, 0);
m_titleLabel = new QLabel("Viewport", m_titleBar);
auto* closeBtn = new QToolButton(m_titleBar);
closeBtn->setText("X");
closeBtn->setFixedSize(18, 18);
closeBtn->setStyleSheet("QToolButton { border: none; font-weight: bold; background: transparent; color: #ccc; } "
"QToolButton:hover { color: white; background: red; }");
titleLayout->addWidget(m_titleLabel);
titleLayout->addStretch();
titleLayout->addWidget(closeBtn);
m_layout->addWidget(m_titleBar);
m_titleBar->setContextMenuPolicy(Qt::CustomContextMenu);
connect(m_titleBar, &QWidget::customContextMenuRequested, this, &QViewportPane::showContextMenu);
connect(closeBtn, &QToolButton::clicked, this, &QViewportPane::onCloseRequested);
addVtkViewport(); // Initialize with a default VTK viewport
}
QViewportPane::~QViewportPane() {}
void QViewportPane::setViewport(QWidget* viewport, const QString& title) {
if (m_viewport) {
m_layout->removeWidget(m_viewport);
delete m_viewport;
}
m_viewport = viewport;
m_titleLabel->setText(title);
m_viewport->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
m_layout->addWidget(m_viewport);
}
void QViewportPane::addVtkViewport() {
auto* viewport = new uLib::Vtk::QViewport(this);
setViewport(viewport, "VTK Viewport");
}
void QViewportPane::addRootCanvas() {
auto* canvas = new uLib::Root::QCanvas(this);
setViewport(canvas, "ROOT Canvas");
}
void QViewportPane::onCloseRequested() {
QSplitter* parentSplitter = qobject_cast<QSplitter*>(parentWidget());
if (parentSplitter && parentSplitter->count() > 1) {
deleteLater();
} else {
// Can't close the last viewport in the splitter safely. Re-initialize to default VTK canvas.
addVtkViewport();
}
}
void QViewportPane::showContextMenu(const QPoint& pos) {
QMenu menu(this);
QAction* hSplit = menu.addAction("H split");
QAction* vSplit = menu.addAction("V split");
menu.addSeparator();
bool isVtk = (qobject_cast<uLib::Vtk::QViewport*>(m_viewport) != nullptr);
QAction* changeType = menu.addAction(isVtk ? "Change to ROOT Canvas" : "Change to VTK Viewport");
QAction* selected = menu.exec(m_titleBar->mapToGlobal(pos));
if (selected == hSplit) {
AttemptSplit(Qt::Horizontal);
} else if (selected == vSplit) {
AttemptSplit(Qt::Vertical);
} else if (selected == changeType) {
if (isVtk) {
addRootCanvas();
} else {
addVtkViewport();
}
}
}
void QViewportPane::AttemptSplit(Qt::Orientation orientation) {
QWidget* p = parentWidget();
if (!p) return;
QSplitter* parentSplitter = qobject_cast<QSplitter*>(p);
if (!parentSplitter) return;
QViewportPane* newPane = new QViewportPane();
// 1. Synchronize viewport content and camera (VTK Viewport only for now)
auto* currentVtk = qobject_cast<uLib::Vtk::QViewport*>(m_viewport);
if (currentVtk) {
auto* newVtk = qobject_cast<uLib::Vtk::QViewport*>(newPane->currentViewport());
if (newVtk) {
// Copy puppets
for (auto* puppet : currentVtk->getPuppets()) {
newVtk->AddPuppet(*puppet);
}
// Copy camera
if (currentVtk->GetRenderer() && newVtk->GetRenderer()) {
vtkCamera* currentCam = currentVtk->GetRenderer()->GetActiveCamera();
vtkCamera* newCam = newVtk->GetRenderer()->GetActiveCamera();
if (currentCam && newCam) {
newCam->DeepCopy(currentCam);
}
}
// Sync grid visible and axis
newVtk->SetGridVisible(currentVtk->GetGridVisible());
newVtk->SetGridAxis(currentVtk->GetGridAxis());
}
}
// 2. Adjust for ROOT Canvas if that was the active view
bool isRoot = (qobject_cast<uLib::Root::QCanvas*>(m_viewport) != nullptr);
if (isRoot) {
newPane->addRootCanvas();
}
if (parentSplitter->orientation() == orientation) {
int index = parentSplitter->indexOf(this);
QList<int> sizes = parentSplitter->sizes();
int currentSize = sizes.value(index, 0);
int half = currentSize / 2;
sizes[index] = half;
sizes.insert(index + 1, currentSize - half);
parentSplitter->insertWidget(index + 1, newPane);
parentSplitter->setSizes(sizes);
} else {
int index = parentSplitter->indexOf(this);
QList<int> parentSizes = parentSplitter->sizes();
QSplitter* newSplitter = new QSplitter(orientation);
newSplitter->addWidget(this);
newSplitter->addWidget(newPane);
QList<int> subSizes;
subSizes << 500 << 500;
newSplitter->setSizes(subSizes);
parentSplitter->insertWidget(index, newSplitter);
parentSplitter->setSizes(parentSizes);
}
}

View File

@@ -0,0 +1,34 @@
#ifndef QVIEWPORTPANE_H
#define QVIEWPORTPANE_H
#include <QWidget>
class QVBoxLayout;
class QLabel;
class QViewportPane : public QWidget {
Q_OBJECT
public:
explicit QViewportPane(QWidget* parent = nullptr);
virtual ~QViewportPane();
void addVtkViewport();
void addRootCanvas();
QWidget* currentViewport() const { return m_viewport; }
private slots:
void onCloseRequested();
void showContextMenu(const QPoint& pos);
private:
void AttemptSplit(Qt::Orientation orientation);
void setViewport(QWidget* viewport, const QString& title);
QVBoxLayout* m_layout;
QWidget* m_titleBar;
QLabel* m_titleLabel;
QWidget* m_viewport;
};
#endif // QVIEWPORTPANE_H

52
app/gcompose/src/main.cpp Normal file
View File

@@ -0,0 +1,52 @@
#include <QApplication>
#include "MainWindow.h"
#include "MainPanel.h"
#include "QViewportPane.h"
#include "Math/ContainerBox.h"
#include <HEP/Geant/Scene.h>
#include "HEP/Detectors/DetectorChamber.h"
#include "Vtk/HEP/Detectors/vtkDetectorChamber.h"
#include <Vtk/vtkContainerBox.h>
#include <Vtk/vtkQViewport.h>
#include <vtkSmartPointer.h>
#include <vtkCubeSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include "Math/Units.h"
#include <iostream>
using namespace uLib;
using namespace uLib::literals;
int main(int argc, char** argv) {
QApplication app(argc, argv);
std::cout << "Starting gcompose Qt application..." << std::endl;
ContainerBox world_box(Vector3f(1, 1, 1));
world_box.Scale(Vector3f(20_mm, 20_mm, 20_mm));
Geant::Scene scene;
scene.ConstructWorldBox(world_box.GetSize(), "G4_AIR");
scene.Initialize();
// 2. Initialize MainWindow (contains embedded VTK QViewport)
MainWindow window;
MainPanel* panel = window.getPanel();
QViewportPane* pane = panel->getFirstPane();
Vtk::QViewport* viewport = qobject_cast<Vtk::QViewport*>(pane->currentViewport());
Vtk::vtkContainerBox vtk_box(&world_box);
viewport->AddPuppet(vtk_box);
viewport->ZoomAuto();
std::cout << "Geant4 and VTK scenes are ready." << std::endl;
window.show();
return app.exec();
}

View File

@@ -1,8 +1,13 @@
[requires]
eigen/3.4.0
boost/1.83.0
pybind11/3.0.2
# pybind11/3.0.2
hdf5/1.14.3
[generators]
CMakeDeps
CMakeToolchain
[options]
hdf5/*:threadsafe=True
hdf5/*:enable_unsupported=True

View File

@@ -49,7 +49,7 @@ endif()
target_link_libraries(${libname} ${LIBRARIES})
install(TARGETS ${libname}
EXPORT "${PROJECT_NAME}Targets"
EXPORT "uLibTargets"
RUNTIME DESTINATION ${INSTALL_BIN_DIR} COMPONENT bin
LIBRARY DESTINATION ${INSTALL_LIB_DIR} COMPONENT lib)

View File

@@ -119,8 +119,8 @@ public:
void AddAdapter(AdapterInterface &ad) { m_a.push_back(Adapter(ad)); }
void Update() {
foreach(Adapter &ad, m_a) {
foreach(DItem &item, m_v) {
for(Adapter &ad : m_a) {
for(DItem &item : m_v) {
item.m_adapter->operator()(ad, item.m_value);
}
}

View File

@@ -145,6 +145,8 @@ GenericMFPtr *Object::findSlotImpl(const char *name) const {
return NULL;
}
void Object::Updated() { ULIB_SIGNAL_EMIT(Object::Updated); }
// std::ostream &
// operator << (std::ostream &os, uLib::Object &ob)
// {

View File

@@ -97,6 +97,9 @@ public:
////////////////////////////////////////////////////////////////////////////
// SIGNALS //
signals:
virtual void Updated();
// Qt4 style connector //
static bool connect(const Object *ob1, const char *signal_name,
const Object *receiver, const char *slot_name) {
@@ -121,6 +124,25 @@ public:
return true;
}
// Lambda/Function object connector //
template <typename Func1, typename SlotT>
static bool connect(typename FunctionPointer<Func1>::Object *sender,
Func1 sigf, SlotT slof) {
SignalBase *sigb = sender->findOrAddSignal(sigf);
typedef typename FunctionPointer<Func1>::SignalSignature SigSignature;
typedef typename Signal<SigSignature>::type SigT;
reinterpret_cast<SigT *>(sigb)->connect(slof);
return true;
}
template <typename Func1, typename Func2>
static bool
disconnect(typename FunctionPointer<Func1>::Object *sender, Func1 sigf,
typename FunctionPointer<Func2>::Object *receiver, Func2 slof) {
// TODO: implement actual disconnect in Signal.h //
return true;
}
template <typename FuncT>
static inline bool connect(SignalBase *sigb, FuncT slof, Object *receiver) {
ConnectSignal<typename FunctionPointer<FuncT>::SignalSignature>(sigb, slof,

View File

@@ -1,12 +0,0 @@
set(HEADERS MuonScatter.h MuonError.h MuonEvent.h)
set(ULIB_SELECTED_MODULES ${ULIB_SELECTED_MODULES} Detectors PARENT_SCOPE)
install(FILES ${HEADERS}
DESTINATION ${INSTALL_INC_DIR}/Detectors)
if(BUILD_TESTING)
include(uLibTargetMacros)
add_subdirectory(testing)
endif()

View File

@@ -1,114 +0,0 @@
/*//////////////////////////////////////////////////////////////////////////////
// CMT Cosmic Muon Tomography project //////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
Copyright (c) 2014, Universita' degli Studi di Padova, INFN sez. di Padova
All rights reserved
Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it >
------------------------------------------------------------------
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library.
//////////////////////////////////////////////////////////////////////////////*/
// G4 Solid //
#include <Geant4/G4Material.hh>
#include <Geant4/G4NistManager.hh>
#include <Geant4/G4LogicalVolume.hh>
// Tessellated solid //
#include <Geant4/G4TessellatedSolid.hh>
#include <Geant4/G4TriangularFacet.hh>
#include <Geant4/G4ThreeVector.hh>
#include "Math/Dense.h"
#include "Solid.h"
namespace uLib {
class DetectorsSolidPimpl {
public:
static G4ThreeVector getG4Vector3f(const Vector3f &vector) {
return G4ThreeVector( vector(0), vector(1), vector(2) );
}
};
Solid::Solid() :
m_Logical (new G4LogicalVolume(NULL,NULL,"unnamed_solid")),
m_Material(NULL)
{}
Solid::Solid(const char *name) :
m_Logical(new G4LogicalVolume(NULL,NULL,name)),
m_Material(NULL)
{}
void Solid::SetNistMaterial(const char *name)
{
G4NistManager *nist = G4NistManager::Instance();
if (m_Material) delete m_Material;
m_Material = nist->FindOrBuildMaterial(name);
m_Logical->SetMaterial(m_Material);
}
void Solid::SetMaterial(G4Material *material)
{
if(material)
{
m_Material = material;
m_Logical->SetMaterial(material);
}
}
TessellatedSolid::TessellatedSolid(const char *name) :
BaseClass(name),
m_Solid(new G4TessellatedSolid(name))
{}
void TessellatedSolid::SetMesh(TriangleMesh &mesh)
{
G4TessellatedSolid *ts = this->m_Solid;
for (int i=0; i<mesh.Triangles().size(); ++i) {
const Vector3i &trg = mesh.Triangles().at(i);
G4TriangularFacet *facet = new G4TriangularFacet(
DetectorsSolidPimpl::getG4Vector3f(mesh.Points().at(trg(0))),
DetectorsSolidPimpl::getG4Vector3f(mesh.Points().at(trg(1))),
DetectorsSolidPimpl::getG4Vector3f(mesh.Points().at(trg(2))),
ABSOLUTE);
ts->AddFacet((G4VFacet *)facet);
}
this->m_Logical->SetSolid(ts);
}
}

View File

@@ -1,16 +0,0 @@
include $(top_srcdir)/Common.am
#AM_DEFAULT_SOURCE_EXT = .cpp
# if HAVE_CHECK
TESTS = GDMLSolidTest
# else
# TEST =
# endif
LDADD = $(top_srcdir)/libmutom-${PACKAGE_VERSION}.la
check_PROGRAMS = $(TESTS)

13
src/HEP/CMakeLists.txt Normal file
View File

@@ -0,0 +1,13 @@
################################################################################
##### HEP - High Energy Physics modules ########################################
################################################################################
include_directories(${SRC_DIR}/HEP)
add_subdirectory(Detectors)
add_subdirectory(Geant)
# add_subdirectory(MuonTomography)
set(ULIB_SHARED_LIBRARIES ${ULIB_SHARED_LIBRARIES} PARENT_SCOPE)
set(ULIB_SELECTED_MODULES ${ULIB_SELECTED_MODULES} PARENT_SCOPE)

View File

@@ -0,0 +1,46 @@
set(HEADERS
ChamberHitEvent.h
DetectorChamber.h
ExperimentFitEvent.h
HierarchicalEncoding.h
Hit.h
HitMC.h
LinearFit.h
MuonError.h
MuonEvent.h
MuonScatter.h
)
set(SOURCES
DetectorChamber.cpp
)
set(LIBRARIES
${PACKAGE_LIBPREFIX}Core
${PACKAGE_LIBPREFIX}Math
)
set(libname ${PACKAGE_LIBPREFIX}Detectors)
set(ULIB_SHARED_LIBRARIES ${ULIB_SHARED_LIBRARIES} ${libname} PARENT_SCOPE)
set(ULIB_SELECTED_MODULES ${ULIB_SELECTED_MODULES} Detectors PARENT_SCOPE)
## SHARED library
add_library(${libname} SHARED ${SOURCES})
set_target_properties(${libname} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_SOVERSION}
CXX_STANDARD 17)
target_link_libraries(${libname} PRIVATE ${LIBRARIES})
install(TARGETS ${libname}
EXPORT "uLibTargets"
RUNTIME DESTINATION ${INSTALL_BIN_DIR} COMPONENT bin
LIBRARY DESTINATION ${INSTALL_LIB_DIR} COMPONENT lib)
install(FILES ${HEADERS}
DESTINATION ${INSTALL_INC_DIR}/HEP/Detectors)
if(BUILD_TESTING)
include(uLibTargetMacros)
add_subdirectory(testing)
endif()

View File

@@ -29,8 +29,8 @@
#define U_CHAMBERHITEVENT_H
#include "Core/Vector.h"
#include "Hit.h"
#include "ChamberDetector.h"
#include "Detectors/HitMC.h"
#include "Detectors/DetectorChamber.h"
namespace uLib {
@@ -38,17 +38,17 @@ class ChamberHitEventData
{
public:
uLibConstRefMacro (Hits, Vector<HitData> )
uLibGetMacro (Idv, ChamberDetector::ID)
uLibGetMacro (Idv, uint)
private:
friend class ChamberHitEvent;
Vector<HitData> m_Hits;
DetectorChamber::ID m_Idv; // -> chamber/view
uint m_Idv; // -> chamber/view
};
class ChamberHitEvent : public ChamberHitEventData {
public:
uLibRefMacro (Hits, Vector<HitData> )
uLibSetMacro (Idv, ChamberDetector::ID)
uLibSetMacro (Idv, uint)
};
}

View File

@@ -0,0 +1,46 @@
#include "HEP/Detectors/DetectorChamber.h"
#include <cmath>
namespace uLib {
MuonEvent DetectorChamber::ProjectMuonEvent(const MuonEvent &muon) const {
MuonEvent projectedMuon = muon;
// Transform the local projection plane to world coordinates
HLine3f worldPlane = this->GetWorldProjectionPlane();
HPoint3f P = worldPlane.origin;
HVector3f N = worldPlane.direction;
HPoint3f X_in = muon.LineIn().origin;
HPoint3f X_out = muon.LineOut().origin;
// Let's use distance to the plane for the first part and keep the logic consistent.
float dist_in = std::abs((X_in - P).dot(N));
float dist_out = std::abs((X_out - P).dot(N));
const HLine3f &chosenLine = (dist_in <= dist_out) ? muon.LineIn() : muon.LineOut();
HPoint3f X_chosen = chosenLine.origin;
// Project X_chosen into the plane defined by P and normal N
// X_proj = X_chosen - ((X_chosen - P) . N / (N . N)) * N
float dot = (X_chosen - P).dot(N);
float n_sq = N.dot(N);
HPoint3f X_proj = X_chosen;
if (n_sq > 0) {
X_proj = X_chosen - (dot / n_sq) * N;
}
// Define the projected line with projected origin and original direction
HLine3f projectedLine;
projectedLine.origin = X_proj;
projectedLine.direction = chosenLine.direction;
// Set both input and output lines of the projected muon to the same projected line
projectedMuon.LineIn() = projectedLine;
projectedMuon.LineOut() = projectedLine;
return projectedMuon;
}
} // namespace uLib

View File

@@ -0,0 +1,84 @@
/*//////////////////////////////////////////////////////////////////////////////
// CMT Cosmic Muon Tomography project //////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
Copyright (c) 2014, Universita' degli Studi di Padova, INFN sez. di Padova
All rights reserved
Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it >
------------------------------------------------------------------
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library.
//////////////////////////////////////////////////////////////////////////////*/
#ifndef U_CHAMBERDETECTOR_H
#define U_CHAMBERDETECTOR_H
#include "Core/Types.h"
#include "Math/ContainerBox.h"
#include "HEP/Detectors/Hit.h"
#include "HEP/Detectors/HitMC.h"
#include "HEP/Detectors/MuonEvent.h"
namespace uLib {
class DetectorChamber : public ContainerBox {
typedef ContainerBox BaseClass;
public:
DetectorChamber() : BaseClass() {
m_ProjectionPlane.origin = HPoint3f(0, 0, 0);
m_ProjectionPlane.direction = HVector3f(0, 0, 1);
}
DetectorChamber(const Vector3f &size) : BaseClass(size) {
m_ProjectionPlane.origin = HPoint3f(0, 0, 0);
m_ProjectionPlane.direction = HVector3f(0, 0, 1);
}
// set the plane where muons hit is projected
// coordinates are local to the container box
void SetProjectionPlane(const HLine3f &normal) { m_ProjectionPlane = normal; }
const HLine3f &GetProjectionPlane() const { return m_ProjectionPlane; }
HLine3f GetWorldProjectionPlane() const {
HLine3f worldPlane;
Matrix4f M = this->GetWorldMatrix();
worldPlane.origin = M * m_ProjectionPlane.origin;
HVector3f dirNorm = M * m_ProjectionPlane.direction;
dirNorm.normalize(); // Normalize for consistent dot products
worldPlane.direction = dirNorm;
return worldPlane;
}
MuonEvent ProjectMuonEvent(const MuonEvent &muon) const;
private:
HLine3f m_ProjectionPlane;
};
}
#endif // CHAMBERDETECTOR_H

View File

@@ -33,9 +33,6 @@
namespace uLib {
class HitRawCode_CMSDrift :
public BitCode4<unsigned short,6,3,2,5>
{
@@ -59,7 +56,13 @@ public:
};
class HitData {
public:
HitData() {}
~HitData() {}
};

View File

@@ -48,6 +48,10 @@ protected:
class MuonEvent : public MuonEventData {
public:
using MuonEventData::LineIn;
using MuonEventData::LineOut;
using MuonEventData::GetMomentum;
inline HLine3f & LineIn() { return this->m_LineIn; }
inline HLine3f & LineOut() { return this->m_LineOut; }
inline Scalarf & Momentum() { return this->m_Momentum; }

View File

@@ -1,12 +1,13 @@
# TESTS
set( TESTS
# GDMLSolidTest
HierarchicalEncodingTest
DetectorChamberTest
)
set(LIBRARIES
${PACKAGE_LIBPREFIX}Core
${PACKAGE_LIBPREFIX}Math
${PACKAGE_LIBPREFIX}Detectors
Boost::serialization
Boost::program_options
Eigen3::Eigen

View File

@@ -0,0 +1,139 @@
/*//////////////////////////////////////////////////////////////////////////////
// CMT Cosmic Muon Tomography project //////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
Copyright (c) 2014, Universita' degli Studi di Padova, INFN sez. di Padova
All rights reserved
Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it >
------------------------------------------------------------------
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library.
//////////////////////////////////////////////////////////////////////////////*/
#include "HEP/Detectors/DetectorChamber.h"
#include "testing-prototype.h"
#include <iostream>
using namespace uLib;
int main() {
BEGIN_TESTING(DetectorChamber Projection);
DetectorChamber chamber;
// Define a horizontal plane at z = 100
HLine3f plane;
plane.origin = HPoint3f(0, 0, 100);
plane.direction = HVector3f(0, 0, 1); // Normal to the plane
chamber.SetProjectionPlane(plane);
// Create a muon with two segments
MuonEvent muon;
// Segment 1 (muon.LineIn()): origin is at (10, 20, 50), direction along Z
muon.LineIn().origin = HPoint3f(10, 20, 50);
muon.LineIn().direction = HVector3f(0, 0, 1);
// Segment 2 (muon.LineOut()): origin is at (10, 20, 200), direction along Z
muon.LineOut().origin = HPoint3f(10, 20, 200);
muon.LineOut().direction = HVector3f(0, 0, 1);
// distance_in = |50 - 100| = 50
// distance_out = |200 - 100| = 100
// LineIn is closer to the plane (z=100)
MuonEvent projected = chamber.ProjectMuonEvent(muon);
// Expected:
// chosenLine = LineIn (it is closer)
// X_chosen = (10, 20, 50)
// X_proj = (10, 20, 50) - (( (10, 20, 50) - (0, 0, 100) ) . (0, 0, 1)) * (0, 0, 1)
// X_proj = (10, 20, 50) - (-50) * (0, 0, 1) = (10, 20, 100)
HPoint3f expectedPos(10, 20, 100);
std::cout << "Test Case 1: LineIn is closer" << std::endl;
std::cout << "Projected Position: " << projected.LineIn().origin.transpose() << std::endl;
std::cout << "Expected Position: " << expectedPos.transpose() << std::endl;
// Check if the projected position is correct
// norm() includes the 4th component, which for HVector3f (diff of points) should be 0.
bool posOk1 = (projected.LineIn().origin - expectedPos).norm() < 1e-5;
TEST1(posOk1);
// Check if LineIn and LineOut are the same
bool linesMatch1 = (projected.LineIn().origin == projected.LineOut().origin) &&
(projected.LineIn().direction == projected.LineOut().direction);
TEST1(linesMatch1);
// Test Case 2: LineOut is closer
muon.LineIn().origin = HPoint3f(30, 40, 0); // dist = 100
muon.LineOut().origin = HPoint3f(30, 40, 110); // dist = 10
projected = chamber.ProjectMuonEvent(muon);
expectedPos = HPoint3f(30, 40, 100); // projection of (30,40,110) onto z=100
std::cout << "\nTest Case 2: LineOut is closer" << std::endl;
std::cout << "Projected Position: " << projected.LineIn().origin.transpose() << std::endl;
std::cout << "Expected Position: " << expectedPos.transpose() << std::endl;
bool posOk2 = (projected.LineIn().origin - expectedPos).norm() < 1e-5;
TEST1(posOk2);
// Test Case 3: Oblique plane
// Plane through (0,0,0) with normal (1,1,1) (normalized)
plane.origin = HPoint3f(0, 0, 0);
plane.direction = HVector3f(1, 1, 1).normalized();
chamber.SetProjectionPlane(plane);
muon.LineIn().origin = HPoint3f(1, 1, 1); // dist = (1,1,1) . (1,1,1).norm()
muon.LineIn().direction = HVector3f(0, 0, 1);
muon.LineOut().origin = HPoint3f(10, 10, 10); // dist = 10 * sqrt(3)
projected = chamber.ProjectMuonEvent(muon);
// X_chosen = (1,1,1)
// X_proj = (1,1,1) - ((1,1,1) . N) * N where N = (1,1,1)/sqrt(3)
// X_proj = (1,1,1) - (sqrt(3)) * (1,1,1)/sqrt(3) = (1,1,1) - (1,1,1) = (0,0,0)
expectedPos = HPoint3f(0, 0, 0);
std::cout << "\nTest Case 3: Oblique plane" << std::endl;
std::cout << "Projected Position: " << projected.LineIn().origin.transpose() << std::endl;
std::cout << "Expected Position: " << expectedPos.transpose() << std::endl;
bool posOk3 = (projected.LineIn().origin - expectedPos).norm() < 1e-5;
TEST1(posOk3);
// Test Case 4: Transformed DetectorChamber
DetectorChamber chamber2;
chamber2.SetPosition(Vector3f(0, 0, 100)); // Move chamber to z=100
// chamber2.GetProjectionPlane has default origin (0,0,0) and direction (0,0,1)
// In world coordinates, this plane is at z = 100 + 0 = 100.
muon.LineIn().origin = HPoint3f(50, 60, 50); // dist to world plane (z=100) is 50
muon.LineOut().origin = HPoint3f(50, 60, 200); // dist to world plane (z=100) is 100
projected = chamber2.ProjectMuonEvent(muon);
expectedPos = HPoint3f(50, 60, 100);
std::cout << "\nTest Case 4: Transformed DetectorChamber (active world matrix)" << std::endl;
std::cout << "Projected Position: " << projected.LineIn().origin.transpose() << std::endl;
std::cout << "Expected Position: " << expectedPos.transpose() << std::endl;
bool posOk4 = (projected.LineIn().origin - expectedPos).norm() < 1e-5;
TEST1(posOk4);
END_TESTING;
}

View File

@@ -0,0 +1,31 @@
#include "ActionInitialization.hh"
#include "EmitterPrimary.hh"
#include "SteppingAction.hh"
namespace uLib {
namespace Geant {
ActionInitialization::ActionInitialization(EmitterPrimary *emitter, SimulationContext *context)
: G4VUserActionInitialization(),
m_Emitter(emitter),
m_Context(context)
{}
ActionInitialization::~ActionInitialization() {}
void ActionInitialization::BuildForMaster() const {}
void ActionInitialization::Build() const {
if (m_Emitter) {
SetUserAction(m_Emitter->Clone());
} else {
SetUserAction(new EmitterPrimary());
}
SteppingAction *sa = new SteppingAction(m_Context);
SetUserAction(static_cast<G4UserSteppingAction*>(sa));
SetUserAction(static_cast<G4UserEventAction*>(sa));
}
} // namespace Geant
} // namespace uLib

View File

@@ -0,0 +1,28 @@
#ifndef ActionInitialization_h
#define ActionInitialization_h
#include "G4VUserActionInitialization.hh"
#include "SimulationContext.h"
namespace uLib {
namespace Geant {
class EmitterPrimary;
class ActionInitialization : public G4VUserActionInitialization {
public:
ActionInitialization(EmitterPrimary *emitter, SimulationContext *context);
~ActionInitialization();
virtual void BuildForMaster() const override;
virtual void Build() const override;
private:
EmitterPrimary *m_Emitter;
SimulationContext *m_Context;
};
} // namespace Geant
} // namespace uLib
#endif

View File

@@ -0,0 +1,74 @@
################################################################################
##### HEP/Geant - Geant4 integration library ###################################
################################################################################
if(NOT Geant4_FOUND)
message(STATUS "Geant4 not found - skipping mutomGeant library")
return()
endif()
message(STATUS "Geant4 found: ${Geant4_VERSION}")
include(${Geant4_USE_FILE})
set(HEADERS
GeantEvent.h
Matter.h
Scene.h
Solid.h
DetectorConstruction.hh
PhysicsList.hh
ActionInitialization.hh
SteppingAction.hh
SimulationContext.h
)
set(SOURCES
Scene.cpp
Solid.cpp
EmitterPrimary.cpp
DetectorConstruction.cpp
PhysicsList.cpp
ActionInitialization.cpp
SteppingAction.cpp
)
set(libname ${PACKAGE_LIBPREFIX}Geant)
set(ULIB_SHARED_LIBRARIES ${ULIB_SHARED_LIBRARIES} ${libname} PARENT_SCOPE)
set(ULIB_SELECTED_MODULES ${ULIB_SELECTED_MODULES} Geant PARENT_SCOPE)
add_library(${libname} SHARED ${SOURCES})
set_target_properties(${libname} PROPERTIES
VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_SOVERSION})
target_include_directories(${libname} PRIVATE ${Geant4_INCLUDE_DIRS})
target_link_libraries(${libname}
${PACKAGE_LIBPREFIX}Core
${PACKAGE_LIBPREFIX}Math
${PACKAGE_LIBPREFIX}Detectors
)
# Filter Geant4 libraries to remove Qt-dependent ones
set(Geant4_LIBS_FILTERED ${Geant4_LIBRARIES})
if(Geant4_LIBS_FILTERED)
list(REMOVE_ITEM Geant4_LIBS_FILTERED Geant4::G4interfaces Geant4::G4OpenGL Geant4::G4visQt3D)
endif()
target_link_libraries(${libname}
${Geant4_LIBS_FILTERED}
)
install(TARGETS ${libname}
EXPORT "uLibTargets"
RUNTIME DESTINATION ${INSTALL_BIN_DIR} COMPONENT bin
LIBRARY DESTINATION ${INSTALL_LIB_DIR} COMPONENT lib)
install(FILES ${HEADERS}
DESTINATION ${INSTALL_INC_DIR}/HEP/Geant)
if(BUILD_TESTING)
include(uLibTargetMacros)
add_subdirectory(testing)
endif()

View File

@@ -0,0 +1,43 @@
#include "DetectorActionInitialization.hh"
#include "EmitterPrimary.hh"
#include "DetectorSteppingAction.hh"
namespace uLib {
namespace Geant {
DetectorActionInitialization::DetectorActionInitialization(EmitterPrimary *emitter,
Vector<MuonEvent> *output,
const Vector<HLine3f> &planes,
int verbosity)
: G4VUserActionInitialization(),
m_Emitter(emitter),
m_Output(output),
m_Planes(planes),
m_Verbosity(verbosity)
{}
DetectorActionInitialization::~DetectorActionInitialization() {}
void DetectorActionInitialization::BuildForMaster() const {}
void DetectorActionInitialization::Build() const {
if (m_Verbosity > 0) {
std::cout << "[Geant] Worker thread Building actions... Output ptr: " << m_Output
<< ", Planes count: " << m_Planes.size() << std::endl;
}
if (m_Emitter) {
SetUserAction(m_Emitter->Clone());
} else {
SetUserAction(new EmitterPrimary());
}
if (m_Output) {
DetectorSteppingAction *sa = new DetectorSteppingAction(m_Output, m_Planes);
sa->SetVerbosity(m_Verbosity);
SetUserAction(static_cast<G4UserSteppingAction*>(sa));
SetUserAction(static_cast<G4UserEventAction*>(sa));
}
}
} // namespace Geant
} // namespace uLib

View File

@@ -0,0 +1,35 @@
#ifndef U_GEANT_DETECTORACTIONINITIALIZATION_HH
#define U_GEANT_DETECTORACTIONINITIALIZATION_HH
#include "G4VUserActionInitialization.hh"
#include "Core/Vector.h"
#include "HEP/Detectors/MuonEvent.h"
#include "Math/Dense.h"
namespace uLib {
namespace Geant {
class EmitterPrimary;
class DetectorActionInitialization : public G4VUserActionInitialization {
public:
DetectorActionInitialization(EmitterPrimary *emitter,
Vector<MuonEvent> *output,
const Vector<HLine3f> &planes,
int verbosity = 0);
~DetectorActionInitialization();
virtual void BuildForMaster() const override;
virtual void Build() const override;
private:
EmitterPrimary *m_Emitter;
Vector<MuonEvent> *m_Output;
Vector<HLine3f> m_Planes;
int m_Verbosity;
};
} // namespace Geant
} // namespace uLib
#endif

View File

@@ -0,0 +1,26 @@
#include "DetectorConstruction.hh"
#include "Core/Object.h"
#include "Math/ContainerBox.h"
#include "G4Box.hh"
#include "G4LogicalVolume.hh"
#include "G4NistManager.hh"
#include "G4PVPlacement.hh"
#include "G4RunManager.hh"
#include "G4SystemOfUnits.hh"
namespace uLib {
namespace Geant {
DetectorConstruction::DetectorConstruction(const char *name) : G4VUserDetectorConstruction() {}
DetectorConstruction::~DetectorConstruction() {}
G4VPhysicalVolume *DetectorConstruction::Construct() { return nullptr; }
void DetectorConstruction::ConstructSDandField() {}
} // namespace Geant
} // namespace uLib

View File

@@ -0,0 +1,30 @@
#ifndef DetectorConstruction_h
#define DetectorConstruction_h
#include "Core/Object.h"
#include "G4VUserDetectorConstruction.hh"
#include "globals.hh"
class G4VPhysicalVolume;
class G4LogicalVolume;
namespace uLib {
namespace Geant {
class DetectorConstruction : public G4VUserDetectorConstruction {
public:
DetectorConstruction(const char *name);
virtual ~DetectorConstruction();
virtual G4VPhysicalVolume *Construct();
virtual void ConstructSDandField();
};
} // namespace Geant
} // namespace uLib
#endif

View File

@@ -0,0 +1,110 @@
#include "DetectorSteppingAction.hh"
#include <Geant4/G4Step.hh>
#include <Geant4/G4Track.hh>
#include <Geant4/G4Event.hh>
#include <Geant4/G4SystemOfUnits.hh>
#include <cmath>
#include <mutex>
#include <iostream>
static std::mutex g_DetectorOutputMutex;
namespace uLib {
namespace Geant {
DetectorSteppingAction::DetectorSteppingAction(Vector<MuonEvent> *output, const Vector<HLine3f> &planes)
: G4UserSteppingAction(),
G4UserEventAction(),
m_Output(output),
m_Planes(planes),
m_CrossCount(0),
m_Verbosity(1)
{
// std::cout << "[Geant] SteppingAction created with " << m_Planes.size() << " planes." << std::endl;
}
DetectorSteppingAction::~DetectorSteppingAction() {}
void DetectorSteppingAction::BeginOfEventAction(const G4Event* /*event*/) {
m_CrossCount = 0;
// Initialize with NaN
float nan = std::numeric_limits<float>::quiet_NaN();
m_Current.LineIn().origin = HPoint3f(nan, nan, nan);
m_Current.LineIn().direction = HVector3f(nan, nan, nan);
m_Current.LineOut().origin = HPoint3f(nan, nan, nan);
m_Current.LineOut().direction = HVector3f(nan, nan, nan);
m_Current.Momentum() = nan;
}
void DetectorSteppingAction::EndOfEventAction(const G4Event* /*event*/) {
if (m_Output) {
std::lock_guard<std::mutex> lock(g_DetectorOutputMutex);
m_Output->push_back(m_Current);
}
}
void DetectorSteppingAction::UserSteppingAction(const G4Step *step) {
if (!step) return;
if (!m_Output) {
return;
}
const G4Track *track = step->GetTrack();
if (!track) return;
static size_t step_count = 0;
if (++step_count % 1000 == 0 && m_Verbosity > 0) {
std::cout << "[GeantMT] Processed " << step_count << " total steps across events." << std::endl;
}
// Only consider primary muons
if (track->GetParentID() != 0) return;
// Track the momentum at generation/first step if not set
if (std::isnan(m_Current.Momentum())) {
m_Current.Momentum() = static_cast<Scalarf>(track->GetMomentum().mag() / MeV);
}
G4ThreeVector p1 = step->GetPreStepPoint()->GetPosition();
G4ThreeVector p2 = step->GetPostStepPoint()->GetPosition();
G4ThreeVector dir_g4 = track->GetMomentumDirection();
HPoint3f p1f(p1.x(), p1.y(), p1.z());
HPoint3f p2f(p2.x(), p2.y(), p2.z());
HVector3f dirf(dir_g4.x(), dir_g4.y(), dir_g4.z());
// Check intersection with each detector plane
for (const auto& plane : m_Planes) {
// Plane: origin=O, direction=N (normal)
HPoint3f O = plane.origin;
HVector3f N = plane.direction;
float d1 = (p1f - O).dot(N);
float d2 = (p2f - O).dot(N);
// Check if the step crossed the plane
if ((d1 > 0 && d2 <= 0) || (d1 < 0 && d2 >= 0)) {
// Intersection point t = d1 / (d1 - d2)
float t = d1 / (d1 - d2);
HPoint3f intersection = p1f + t * (p2f - p1f);
if (m_CrossCount == 0) {
m_Current.LineIn().origin = intersection;
m_Current.LineIn().direction = dirf;
m_CrossCount++;
if (m_Verbosity > 0) std::cout << "[GeantMT] Hit first plane at " << intersection.transpose() << std::endl;
} else if (m_CrossCount == 1) {
m_Current.LineOut().origin = intersection;
m_Current.LineOut().direction = dirf;
m_CrossCount++;
if (m_Verbosity > 0) std::cout << "[GeantMT] Hit second plane at " << intersection.transpose() << std::endl;
}
// We break to avoid crossing multiple planes in one infinitesimal step (unlikely but possible)
// Actually, we should check ALL planes.
}
}
}
} // namespace Geant
} // namespace uLib

View File

@@ -0,0 +1,36 @@
#ifndef U_GEANT_DETECTORSTEPPINGACTION_HH
#define U_GEANT_DETECTORSTEPPINGACTION_HH
#include "G4UserSteppingAction.hh"
#include "G4UserEventAction.hh"
#include "Core/Vector.h"
#include "HEP/Detectors/MuonEvent.h"
#include "HEP/Detectors/DetectorChamber.h"
#include <mutex>
namespace uLib {
namespace Geant {
class DetectorSteppingAction : public G4UserSteppingAction, public G4UserEventAction {
public:
DetectorSteppingAction(Vector<MuonEvent> *output, const Vector<HLine3f> &planes);
virtual ~DetectorSteppingAction();
virtual void UserSteppingAction(const G4Step *step) override;
virtual void BeginOfEventAction(const G4Event *event) override;
virtual void EndOfEventAction(const G4Event *event) override;
void SetVerbosity(int level) { m_Verbosity = level; }
private:
Vector<MuonEvent> *m_Output;
Vector<HLine3f> m_Planes; // World projection planes
MuonEvent m_Current;
int m_CrossCount = 0;
int m_Verbosity = 0;
};
} // namespace Geant
} // namespace uLib
#endif

1203
src/HEP/Geant/EcoMug.hh Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,330 @@
#include "EmitterPrimary.hh"
#include "G4Box.hh"
#include "G4LogicalVolume.hh"
#include "G4LogicalVolumeStore.hh"
#include "G4ParticleDefinition.hh"
#include "G4ParticleGun.hh"
#include "G4ParticleTable.hh"
#include "G4RunManager.hh"
#include "G4SystemOfUnits.hh"
#include "Randomize.hh"
#include "EcoMug.hh"
#include "Math/Cylinder.h"
namespace uLib {
namespace Geant {
EmitterPrimary::EmitterPrimary()
: G4VUserPrimaryGeneratorAction(), fParticleGun(nullptr) {
// Creiamo il ParticleGun impostandolo per sparare 1 particella alla volta
G4int n_particle = 1;
fParticleGun = new G4ParticleGun(n_particle);
// Otteniamo la tabella delle particelle di Geant4
G4ParticleTable *particleTable = G4ParticleTable::GetParticleTable();
// Cerchiamo il muone negativo (usa "mu+" per l'antimuone)
G4String particleName = "mu-";
G4ParticleDefinition *particle = particleTable->FindParticle(particleName);
// Configuriamo le proprietà iniziali della particella
fParticleGun->SetParticleDefinition(particle);
// Impostiamo l'energia cinetica a 1 GeV
fParticleGun->SetParticleEnergy(1.0 * GeV);
// Initial position and direction through AffineTransform
// 10m on Z axis, pointing towards origin
this->SetPosition(Vector3f(0, 0, 10000.0));
// Default orientation is identity (pointing along -Z if we rotate the puppet accordingly)
// But fParticleGun defaults are set here and overridden in GeneratePrimaries
}
EmitterPrimary::~EmitterPrimary() {
// Importante: liberare la memoria
delete fParticleGun;
}
void EmitterPrimary::GeneratePrimaries(G4Event *anEvent) {
// Use position and direction from AffineTransform
Vector3f pos = this->GetPosition();
// Assume default direction is along the -Z axis of the local frame
Vector4f dir4 = this->GetWorldMatrix() * Vector4f(0, 0, -1, 0);
Vector3f dir = dir4.head<3>().normalized();
fParticleGun->SetParticlePosition(G4ThreeVector(pos(0), pos(1), pos(2)));
fParticleGun->SetParticleMomentumDirection(G4ThreeVector(dir(0), dir(1), dir(2)));
fParticleGun->GeneratePrimaryVertex(anEvent);
}
EmitterPrimary* EmitterPrimary::Clone() const {
auto* clone = new EmitterPrimary();
clone->SetMatrix(this->GetMatrix());
return clone;
}
// -------------------------------------------------------------------------- //
// SkyPlaneEmitterPrimary using EcoMug
SkyPlaneEmitterPrimary::SkyPlaneEmitterPrimary()
: EmitterPrimary(), m_EcoMug(new EcoMug()), m_Size(1000.0, 1000.0) {
// Initial configuration for EcoMug in sky mode
m_EcoMug->SetUseSky();
m_EcoMug->SetSkySize({m_Size(0)/1000.0, m_Size(1)/1000.0});
}
SkyPlaneEmitterPrimary::~SkyPlaneEmitterPrimary() {
delete m_EcoMug;
}
void SkyPlaneEmitterPrimary::SetSkySize(const uLib::Vector2f& size) {
m_Size = size;
// EcoMug units are in meters (m=1), Geant4 units are in mm.
m_EcoMug->SetSkySize({m_Size(0)/1000.0, m_Size(1)/1000.0});
this->Updated();
}
void SkyPlaneEmitterPrimary::SetPlane(const uLib::Vector3f& p0, const uLib::Vector3f& normal) {
this->SetPosition(p0);
// Orient the emitter so that local Z is the normal.
// This is useful for unconventional planes, though EcoMug sky is usually horizontal.
// If we want a truly 'sky' plane, it usually stays horizontal.
this->Updated();
}
void SkyPlaneEmitterPrimary::GeneratePrimaries(G4Event* anEvent) {
if (!m_EcoMug) return;
m_EcoMug->Generate();
// EcoMug position is relative to its internal sky center in meters.
// Our wrapper uses the AffineTransform for the overall positioning.
std::array<double, 3> pos_m = m_EcoMug->GetGenerationPosition();
G4ThreeVector local_pos(pos_m[0]*1000.0, pos_m[1]*1000.0, pos_m[2]*1000.0);
// EcoMug momentum (direction and magnitude in GeV/c)
double p_mag = m_EcoMug->GetGenerationMomentum();
double theta = m_EcoMug->GetGenerationTheta();
double phi = m_EcoMug->GetGenerationPhi();
// EcoMug theta is generated in a way that PI means pointing down (-Z)
G4ThreeVector local_dir(
sin(theta) * cos(phi),
sin(theta) * sin(phi),
cos(theta)
);
// Transform local coordinates to world
Matrix4f world_mat = this->GetWorldMatrix();
Vector3f world_pos = (world_mat * Vector4f(local_pos.x(), local_pos.y(), local_pos.z(), 1.0)).head<3>();
Vector3f world_dir = (world_mat * Vector4f(local_dir.x(), local_dir.y(), local_dir.z(), 0.0)).head<3>().normalized();
// Set particle charge
G4ParticleTable *particleTable = G4ParticleTable::GetParticleTable();
G4String particleName = (m_EcoMug->GetCharge() > 0) ? "mu+" : "mu-";
fParticleGun->SetParticleDefinition(particleTable->FindParticle(particleName));
fParticleGun->SetParticlePosition(G4ThreeVector(world_pos(0), world_pos(1), world_pos(2)));
fParticleGun->SetParticleMomentumDirection(G4ThreeVector(world_dir(0), world_dir(1), world_dir(2)));
fParticleGun->SetParticleEnergy(p_mag * GeV);
fParticleGun->GeneratePrimaryVertex(anEvent);
}
EmitterPrimary* SkyPlaneEmitterPrimary::Clone() const {
auto* clone = new SkyPlaneEmitterPrimary();
clone->SetSkySize(this->m_Size);
clone->SetMatrix(this->GetMatrix());
return clone;
}
// -------------------------------------------------------------------------- //
// CylinderEmitterPrimary using EcoMug
CylinderEmitterPrimary::CylinderEmitterPrimary()
: EmitterPrimary(), m_EcoMug(new EcoMug()), m_Radius(1000.0), m_Height(1000.0) {
m_EcoMug->SetUseCylinder();
m_EcoMug->SetCylinderRadius(m_Radius/1000.0);
m_EcoMug->SetCylinderHeight(m_Height/1000.0);
m_EcoMug->SetCylinderCenterPosition({0.0, 0.0, m_Height/2000.0});
}
CylinderEmitterPrimary::~CylinderEmitterPrimary() {
delete m_EcoMug;
}
void CylinderEmitterPrimary::SetRadius(float r) {
m_Radius = r;
m_EcoMug->SetCylinderRadius(m_Radius/1000.0);
this->Updated();
}
void CylinderEmitterPrimary::SetHeight(float h) {
m_Height = h;
m_EcoMug->SetCylinderHeight(m_Height/1000.0);
m_EcoMug->SetCylinderCenterPosition({0.0, 0.0, m_Height/2000.0});
this->Updated();
}
void CylinderEmitterPrimary::GeneratePrimaries(G4Event* anEvent) {
if (!m_EcoMug) return;
m_EcoMug->Generate();
std::array<double, 3> pos_m = m_EcoMug->GetGenerationPosition();
G4ThreeVector local_pos(pos_m[0]*1000.0, pos_m[1]*1000.0, pos_m[2]*1000.0);
double p_mag = m_EcoMug->GetGenerationMomentum();
double theta = m_EcoMug->GetGenerationTheta();
double phi = m_EcoMug->GetGenerationPhi();
G4ThreeVector local_dir(
sin(theta) * cos(phi),
sin(theta) * sin(phi),
cos(theta)
);
Matrix4f world_mat = this->GetWorldMatrix();
Vector3f world_pos = (world_mat * Vector4f(local_pos.x(), local_pos.y(), local_pos.z(), 1.0)).head<3>();
Vector3f world_dir = (world_mat * Vector4f(local_dir.x(), local_dir.y(), local_dir.z(), 0.0)).head<3>().normalized();
G4ParticleTable *particleTable = G4ParticleTable::GetParticleTable();
G4String particleName = (m_EcoMug->GetCharge() > 0) ? "mu+" : "mu-";
fParticleGun->SetParticleDefinition(particleTable->FindParticle(particleName));
fParticleGun->SetParticlePosition(G4ThreeVector(world_pos(0), world_pos(1), world_pos(2)));
fParticleGun->SetParticleMomentumDirection(G4ThreeVector(world_dir(0), world_dir(1), world_dir(2)));
fParticleGun->SetParticleEnergy(p_mag * GeV);
fParticleGun->GeneratePrimaryVertex(anEvent);
}
EmitterPrimary* CylinderEmitterPrimary::Clone() const {
auto* clone = new CylinderEmitterPrimary();
clone->SetRadius(this->m_Radius);
clone->SetHeight(this->m_Height);
clone->SetMatrix(this->GetMatrix());
return clone;
}
// -------------------------------------------------------------------------- //
// -------------------------------------------------------------------------- //
QuadMeshEmitterPrimary::QuadMeshEmitterPrimary()
: EmitterPrimary(), m_Mesh(nullptr), m_TotalArea(0.0) {
}
QuadMeshEmitterPrimary::~QuadMeshEmitterPrimary() {
}
void QuadMeshEmitterPrimary::SetMesh(uLib::QuadMesh *mesh) {
m_Mesh = mesh;
CalculateAreas();
}
void QuadMeshEmitterPrimary::CalculateAreas() {
if (!m_Mesh) return;
m_CumulativeAreas.clear();
m_TotalArea = 0.0;
const auto &quads = m_Mesh->Quads();
for (const auto &q : quads) {
uLib::Vector3f v0 = m_Mesh->GetPoint(q(0));
uLib::Vector3f v1 = m_Mesh->GetPoint(q(1));
uLib::Vector3f v2 = m_Mesh->GetPoint(q(2));
uLib::Vector3f v3 = m_Mesh->GetPoint(q(3));
double a1 = 0.5 * (v1 - v0).cross(v2 - v0).norm();
double a2 = 0.5 * (v2 - v0).cross(v3 - v0).norm();
m_TotalArea += (a1 + a2);
m_CumulativeAreas.push_back(m_TotalArea);
}
}
void QuadMeshEmitterPrimary::GeneratePrimaries(G4Event *anEvent) {
if (!m_Mesh || m_TotalArea <= 0.0) return;
// 1. Choose a quad
double r = G4UniformRand() * m_TotalArea;
auto it = std::lower_bound(m_CumulativeAreas.begin(), m_CumulativeAreas.end(), r);
int quadIdx = std::distance(m_CumulativeAreas.begin(), it);
const auto &q = m_Mesh->Quads()[quadIdx];
uLib::Vector3f v0 = m_Mesh->GetPoint(q(0));
uLib::Vector3f v1 = m_Mesh->GetPoint(q(1));
uLib::Vector3f v2 = m_Mesh->GetPoint(q(2));
uLib::Vector3f v3 = m_Mesh->GetPoint(q(3));
// 2. Choose a point on the quad
double a1 = 0.5 * (v1 - v0).cross(v2 - v0).norm();
double a2 = 0.5 * (v2 - v0).cross(v3 - v0).norm();
G4ThreeVector pos;
uLib::Vector3f normal = m_Mesh->GetNormal(quadIdx);
if (G4UniformRand() < a1 / (a1 + a2)) {
double u = G4UniformRand();
double v = G4UniformRand();
if (u + v > 1.0) { u = 1.0 - u; v = 1.0 - v; }
uLib::Vector3f p = v0 + u * (v1 - v0) + v * (v2 - v0);
pos.set(p(0), p(1), p(2));
} else {
double u = G4UniformRand();
double v = G4UniformRand();
if (u + v > 1.0) { u = 1.0 - u; v = 1.0 - v; }
uLib::Vector3f p = v0 + u * (v2 - v0) + v * (v3 - v0);
pos.set(p(0), p(1), p(2));
}
// 3. Choose a direction (Cosmic Muon: cos^2(theta))
G4ThreeVector dir;
bool accepted = false;
int tries = 0;
while (!accepted && tries < 1000) {
tries++;
double cosTheta = std::pow(G4UniformRand(), 1.0/3.0);
double sinTheta = std::sqrt(1.0 - cosTheta * cosTheta);
double phi = 2.0 * M_PI * G4UniformRand();
// Incoming from above (+Z towards -Z)
dir.set(sinTheta * std::cos(phi), sinTheta * std::sin(phi), -cosTheta);
// Filtering: pointing on the same side of the face normal
if (dir.x() * normal(0) + dir.y() * normal(1) + dir.z() * normal(2) > 0) {
accepted = true;
}
}
if (accepted) {
fParticleGun->SetParticlePosition(pos);
fParticleGun->SetParticleMomentumDirection(dir);
// Keep energy from base class or set here if needed
fParticleGun->GeneratePrimaryVertex(anEvent);
}
}
EmitterPrimary* QuadMeshEmitterPrimary::Clone() const {
auto* clone = new QuadMeshEmitterPrimary();
if (m_Mesh) clone->SetMesh(m_Mesh);
clone->SetMatrix(this->GetMatrix());
return clone;
}
} // namespace Geant
} // namespace uLib

View File

@@ -0,0 +1,115 @@
#ifndef U_GEANT_EMITTERPRIMARY_HH
#define U_GEANT_EMITTERPRIMARY_HH 1
#include "G4VUserPrimaryGeneratorAction.hh"
#include "globals.hh"
namespace uLib {
class QuadMesh;
}
class EcoMug;
#include "Math/QuadMesh.h"
#include "Core/Object.h"
#include "Math/Transform.h"
#include <vector> // Added for std::vector
class G4ParticleGun;
class G4Event;
namespace uLib {
namespace Geant {
class EmitterPrimary : public G4VUserPrimaryGeneratorAction, public Object, public AffineTransform
{
public:
EmitterPrimary();
virtual ~EmitterPrimary();
// Metodo principale chiamato all'inizio di ogni evento
virtual void GeneratePrimaries(G4Event*);
virtual void Updated() override { ULIB_SIGNAL_EMIT(EmitterPrimary::Updated); }
/// Create a clone of this emitter for multi-threading
virtual EmitterPrimary* Clone() const;
protected:
G4ParticleGun* fParticleGun; // Puntatore al cannone di particelle
};
class SkyPlaneEmitterPrimary : public EmitterPrimary
{
public:
SkyPlaneEmitterPrimary();
virtual ~SkyPlaneEmitterPrimary();
virtual void GeneratePrimaries(G4Event*);
void SetPlane(const uLib::Vector3f& p0, const uLib::Vector3f& normal);
void SetSkySize(const uLib::Vector2f& size);
uLib::Vector2f GetSkySize() const { return m_Size; }
virtual EmitterPrimary* Clone() const override;
private:
EcoMug* m_EcoMug;
uLib::Vector2f m_Size;
};
class CylinderEmitterPrimary : public EmitterPrimary
{
public:
CylinderEmitterPrimary();
virtual ~CylinderEmitterPrimary();
virtual void GeneratePrimaries(G4Event*);
void SetRadius(float r);
float GetRadius() const { return m_Radius; }
void SetHeight(float h);
float GetHeight() const { return m_Height; }
virtual EmitterPrimary* Clone() const override;
private:
EcoMug* m_EcoMug;
float m_Radius;
float m_Height;
};
class QuadMeshEmitterPrimary : public EmitterPrimary
{
public:
QuadMeshEmitterPrimary();
virtual ~QuadMeshEmitterPrimary();
// Metodo principale chiamato all'inizio di ogni evento
virtual void GeneratePrimaries(G4Event*);
void SetMesh(uLib::QuadMesh* mesh);
virtual EmitterPrimary* Clone() const override;
private:
uLib::QuadMesh* m_Mesh;
std::vector<double> m_CumulativeAreas;
double m_TotalArea;
void CalculateAreas();
};
} // namespace Geant
} // namespace uLib
#endif

View File

@@ -0,0 +1,25 @@
#include "HEP/Geant/GeantEvent.h"
#include <cstddef>
#include <iostream>
using namespace uLib;
void GeantEvent::Print(const size_t size) const {
std::cout << "Event " << m_EventID << ":" << std::endl;
std::cout << " Momentum: " << m_Momentum << std::endl;
std::cout << " GenVector: " << m_GenVector << std::endl;
std::cout << " Path: " << std::endl;
size_t limit = m_Path.size();
if(size > 0 && size < m_Path.size()) {
limit = size;
}
for (size_t i = 0; i < limit; ++i) {
std::cout << " " << i << ": " << m_Path[i].m_Length << " " << m_Path[i].m_Momentum << " " << m_Path[i].m_Direction << " " << m_Path[i].m_SolidName << std::endl;
}
if (limit < m_Path.size()) {
std::cout << " ... (" << m_Path.size() - limit << " more deltas)" << std::endl;
}
}

View File

@@ -0,0 +1,93 @@
/*//////////////////////////////////////////////////////////////////////////////
// CMT Cosmic Muon Tomography project //////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
Copyright (c) 2014, Universita' degli Studi di Padova, INFN sez. di Padova
All rights reserved
Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it >
------------------------------------------------------------------
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library.
//////////////////////////////////////////////////////////////////////////////*/
#ifndef U_GEANTEVENT_H
#define U_GEANTEVENT_H
#include "Core/Types.h"
#include "Core/Vector.h"
#include "Math/Dense.h"
#include <string>
namespace uLib {
namespace Geant {
// Forward declaration for friend access
class SteppingAction;
///////////////////////////////////////////////////////////////////////////////
/// GeantEvent — output of a Geant4 simulation run.
///
/// m_Momentum and m_GenVector are set from the EmitterPrimary at generation.
/// During simulation, each scattering interaction deposits a Delta in m_Path,
/// recording the change of momentum and direction at each step boundary.
///////////////////////////////////////////////////////////////////////////////
class GeantEvent {
public:
/// A single interaction step along the muon path.
struct Delta {
Scalarf m_Length; ///< step length through the solid
Scalarf m_Momentum; ///< momentum magnitude at this step
HVector3f m_Direction; ///< direction after scattering
std::string m_SolidName; ///< name of the solid where interaction occurred
uLibGetMacro(Length, Scalarf)
uLibGetMacro(Momentum, Scalarf)
uLibConstRefMacro(Direction, HVector3f)
uLibConstRefMacro(SolidName, std::string)
Delta() : m_Length(0), m_Momentum(0), m_Direction(HVector3f::Zero()) {}
};
// --- Read-only accessors (public) --- //
uLibGetMacro(EventID, Id_t)
uLibGetMacro(Momentum, Scalarf)
uLibConstRefMacro(GenVector, HLine3f)
uLibConstRefMacro(Path, Vector<Delta>)
void Print(const size_t size = 10) const;
private:
Id_t m_EventID;
Scalarf m_Momentum;
HLine3f m_GenVector;
Vector<Delta> m_Path;
public:
GeantEvent() : m_EventID(0), m_Momentum(0), m_GenVector(HLine3f()), m_Path() {}
// SteppingAction can populate the private fields during simulation
friend class SteppingAction;
};
} // namespace Geant
} // namespace uLib
#endif // U_GEANTEVENT_H

View File

@@ -34,6 +34,7 @@ class G4Element;
class G4Material;
namespace uLib {
namespace Geant {
////////////////////////////////////////////////////////////////////////////////
@@ -65,6 +66,7 @@ private:
}
}

View File

@@ -0,0 +1,28 @@
#include "PhysicsList.hh"
#include "G4DecayPhysics.hh"
#include "G4EmStandardPhysics.hh"
#include "G4RadioactiveDecayPhysics.hh"
namespace uLib {
namespace Geant {
PhysicsList::PhysicsList() : G4VModularPhysicsList() {
SetVerboseLevel(1);
// Default physics
RegisterPhysics(new G4DecayPhysics());
// EM physics
RegisterPhysics(new G4EmStandardPhysics());
// Radioactive decay
RegisterPhysics(new G4RadioactiveDecayPhysics());
}
PhysicsList::~PhysicsList() {}
void PhysicsList::SetCuts() { G4VModularPhysicsList::SetCuts(); }
} // namespace Geant
} // namespace uLib

View File

@@ -0,0 +1,20 @@
#ifndef PhysicsList_h
#define PhysicsList_h
#include "G4VModularPhysicsList.hh"
namespace uLib {
namespace Geant {
class PhysicsList : public G4VModularPhysicsList {
public:
PhysicsList();
virtual ~PhysicsList();
virtual void SetCuts();
};
} // namespace Geant
} // namespace uLib
#endif

160
src/HEP/Geant/Scene.cpp Normal file
View File

@@ -0,0 +1,160 @@
#include <Geant4/G4Box.hh>
#include <Geant4/G4LogicalVolume.hh>
#include <Geant4/G4Material.hh>
#include <Geant4/G4NistManager.hh>
#include <Geant4/G4PVPlacement.hh>
#include <Geant4/G4RunManager.hh>
#include <Geant4/G4RunManagerFactory.hh>
#include <Geant4/G4SystemOfUnits.hh>
#include <Geant4/G4VPhysicalVolume.hh>
#include "Core/Vector.h"
#include "HEP/Geant/DetectorConstruction.hh"
#include "Math/ContainerBox.h"
#include "Math/Dense.h"
#include "Solid.h"
#include "Scene.h"
#include "PhysicsList.hh"
#include "ActionInitialization.hh"
#include "SimulationContext.h"
#include "HEP/Detectors/DetectorChamber.h"
namespace uLib {
namespace Geant {
class SceneDetectorConstruction : public DetectorConstruction {
public:
SceneDetectorConstruction(class SceneImpl *owner) : DetectorConstruction("Scene"), m_Owner(owner) {}
G4VPhysicalVolume *Construct() override;
private:
class SceneImpl *m_Owner;
};
static void CheckGeant4Environment() {
static bool checked = false;
if (checked) return;
checked = true;
if (!std::getenv("G4ENSDFSTATEDATA")) {
std::cerr << "********************************************************" << std::endl;
std::cerr << " WARNING: Geant4 environment variables are not set!" << std::endl;
std::cerr << " Please activate the environment before running:" << std::endl;
std::cerr << " micromamba activate mutom" << std::endl;
std::cerr << "********************************************************" << std::endl;
}
}
class SceneImpl {
public:
SceneImpl() : m_RunManager(G4RunManagerFactory::CreateRunManager(G4RunManagerType::Default)),
m_Emitter(nullptr),
m_InitCalled(false) {
m_RunManager->SetUserInitialization(new PhysicsList);
}
~SceneImpl() {
if (m_RunManager) delete m_RunManager;
// m_World deletion is handled in Scene destructor or here
}
void Initialize() {
if (m_InitCalled) return;
m_RunManager->SetUserInitialization(new SceneDetectorConstruction(this));
m_RunManager->SetUserInitialization(new ActionInitialization(m_Emitter, &m_Context));
m_RunManager->Initialize();
m_InitCalled = true;
}
Vector<Solid *> m_Solids;
Solid *m_World = nullptr;
ContainerBox m_WorldBox;
G4RunManager *m_RunManager;
EmitterPrimary *m_Emitter;
SimulationContext m_Context;
bool m_InitCalled;
};
G4VPhysicalVolume *SceneDetectorConstruction::Construct() {
return m_Owner->m_World->GetPhysical();
}
Scene::Scene() {
CheckGeant4Environment();
d = new SceneImpl();
}
Scene::~Scene() {
// Delete solids
for(auto s : d->m_Solids) delete s;
delete d;
}
void Scene::AddSolid(Solid *solid, Solid *parent) {
d->m_Solids.push_back(solid);
if (!d->m_World) {
d->m_World = solid;
} else {
solid->SetParent(parent ? parent : d->m_World);
}
}
const Solid* Scene::GetWorld() const { return d->m_World; }
ContainerBox* Scene::GetWorldBox() const { return &d->m_WorldBox; }
void Scene::ConstructWorldBox(const Vector3f &size, const char *material) {
d->m_WorldBox.Scale(size);
d->m_WorldBox.SetPosition(-size/2.0f);
if (!d->m_World) {
d->m_World = new Solid("World");
d->m_World->SetNistMaterial(material);
AddSolid(d->m_World);
}
G4Box *solidWorld = new G4Box("World", 0.5 * size(0), 0.5 * size(1), 0.5 * size(2));
G4LogicalVolume *logicWorld = new G4LogicalVolume(solidWorld, d->m_World->GetMaterial(), d->m_World->GetName());
d->m_World->SetLogical(logicWorld);
G4PVPlacement *physWorld = new G4PVPlacement(nullptr, G4ThreeVector(0, 0, 0), logicWorld, d->m_World->GetName(), 0, false, 0, true);
d->m_World->SetPhysical(physWorld);
}
void Scene::SetEmitter(EmitterPrimary *emitter) { d->m_Emitter = emitter; }
void Scene::Initialize() { d->Initialize(); }
void Scene::SetVerbosity(int level) {
d->m_Context.verbosity = level;
if (d->m_RunManager) d->m_RunManager->SetVerboseLevel(level);
}
void Scene::RunSimulation(int nEvents, Vector<GeantEvent> &results) {
d->Initialize(); // Ensure initialized
d->m_Context.mode = SimulationMode::DETAILED;
d->m_Context.outputGeant = &results;
d->m_Context.outputMuon = nullptr;
d->m_RunManager->BeamOn(nEvents);
}
void Scene::RunDetectorSimulation(int nEvents, Vector<MuonEvent> &results) {
d->Initialize(); // Ensure initialized
d->m_Context.mode = SimulationMode::DETECTOR;
d->m_Context.outputGeant = nullptr;
d->m_Context.outputMuon = &results;
// Find detector planes
d->m_Context.detectorPlanes.clear();
for (Solid* s : d->m_Solids) {
if (BoxSolid* bs = dynamic_cast<BoxSolid*>(s)) {
if (DetectorChamber* dc = dynamic_cast<DetectorChamber*>(bs->GetObject())) {
d->m_Context.detectorPlanes.push_back(dc->GetWorldProjectionPlane());
}
}
}
d->m_RunManager->BeamOn(nEvents);
}
} // namespace Geant
} // namespace uLib

81
src/HEP/Geant/Scene.h Normal file
View File

@@ -0,0 +1,81 @@
/*//////////////////////////////////////////////////////////////////////////////
// CMT Cosmic Muon Tomography project //////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
Copyright (c) 2014, Universita' degli Studi di Padova, INFN sez. di Padova
All rights reserved
Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it >
------------------------------------------------------------------
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library.
//////////////////////////////////////////////////////////////////////////////*/
#ifndef SCENE_H
#define SCENE_H
#include "Core/Object.h"
#include "Core/Vector.h"
#include "Solid.h"
#include "GeantEvent.h"
#include "HEP/Detectors/MuonEvent.h"
class G4VPhysicalVolume;
namespace uLib {
namespace Geant {
class EmitterPrimary;
class Scene : public Object {
public:
Scene();
~Scene();
void AddSolid(Solid *solid, Solid *parent = nullptr);
void ConstructWorldBox(const Vector3f &size, const char *material);
/// Get the world box
const Solid* GetWorld() const;
ContainerBox* GetWorldBox() const;
/// Set the primary generator (emitter) for the simulation.
/// The Scene does NOT take ownership of the emitter.
void SetEmitter(EmitterPrimary *emitter);
/// Initialize the Geant4 run manager with detector, physics, and action.
void Initialize();
/// Set the verbosity level for console output (default 0)
void SetVerbosity(int level);
/// Run the simulation for nEvents muons.
/// Results are appended to the provided vector.
void RunSimulation(int nEvents, Vector<GeantEvent> &results);
/// Specialized detector simulation trackingMuonEvent line crossings.
void RunDetectorSimulation(int nEvents, Vector<MuonEvent> &results);
private:
class SceneImpl *d;
};
} // namespace Geant
} // namespace uLib
#endif // SCENE_H

View File

@@ -0,0 +1,30 @@
#ifndef U_GEANT_SIMULATIONCONTEXT_H
#define U_GEANT_SIMULATIONCONTEXT_H
#include "Core/Vector.h"
#include "GeantEvent.h"
#include "HEP/Detectors/MuonEvent.h"
#include "Math/Dense.h"
#include <mutex>
namespace uLib {
namespace Geant {
enum class SimulationMode {
DETAILED,
DETECTOR
};
struct SimulationContext {
SimulationMode mode = SimulationMode::DETAILED;
Vector<GeantEvent> *outputGeant = nullptr;
Vector<MuonEvent> *outputMuon = nullptr;
Vector<HLine3f> detectorPlanes;
int verbosity = 0;
std::mutex outputMutex;
};
} // namespace Geant
} // namespace uLib
#endif

208
src/HEP/Geant/Solid.cpp Normal file
View File

@@ -0,0 +1,208 @@
/*//////////////////////////////////////////////////////////////////////////////
// CMT Cosmic Muon Tomography project //////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
Copyright (c) 2014, Universita' degli Studi di Padova, INFN sez. di Padova
All rights reserved
Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it >
------------------------------------------------------------------
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library.
//////////////////////////////////////////////////////////////////////////////*/
// G4 Solid //
#include <CLHEP/Units/SystemOfUnits.h>
#include <Geant4/G4LogicalVolume.hh>
#include <Geant4/G4Material.hh>
#include <Geant4/G4NistManager.hh>
// Tessellated solid //
#include <Geant4/G4TessellatedSolid.hh>
#include <Geant4/G4ThreeVector.hh>
#include <Geant4/G4TriangularFacet.hh>
#include <Geant4/G4Box.hh>
#include <Geant4/G4PVPlacement.hh>
#include "Math/Dense.h"
#include "Math/Transform.h"
#include "Solid.h"
namespace uLib {
namespace Geant {
class DetectorsSolidImpl {
public:
static G4ThreeVector getG4Vector3f(const Vector3f &vector) {
return G4ThreeVector(vector(0), vector(1), vector(2));
}
};
Solid::Solid()
: m_Name("unnamed_solid"), m_Material(NULL), m_Logical(NULL), m_Physical(NULL),
m_Position(new G4ThreeVector(0,0,0)), m_Rotation(NULL) {}
Solid::Solid(const char *name)
: m_Name(name), m_Material(NULL), m_Logical(NULL), m_Physical(NULL),
m_Position(new G4ThreeVector(0,0,0)), m_Rotation(NULL) {}
Solid::~Solid() {
if (m_Position) delete m_Position;
if (m_Rotation) delete m_Rotation;
}
void Solid::SetNistMaterial(const char *name) {
G4NistManager *nist = G4NistManager::Instance();
G4Material *mat = nist->FindOrBuildMaterial(name);
if (mat) SetMaterial(mat);
}
void Solid::SetMaterial(G4Material *material) {
if (material) {
m_Material = material;
if (m_Logical) {
m_Logical->SetMaterial(material);
} else if (GetG4Solid()) {
m_Logical = new G4LogicalVolume(GetG4Solid(), m_Material, GetName());
}
}
}
void Solid::SetTransform(Matrix4f transform) {
uLib::AffineTransform t;
t.SetMatrix(transform);
// 2. Extract position and rotation for Geant4
Vector3f pos = t.GetPosition();
if (!m_Position) m_Position = new G4ThreeVector();
*m_Position = G4ThreeVector(pos(0), pos(1), pos(2));
// Create a G4 rotation matrix from the 4x4 matrix
Matrix3f m = t.GetRotation();
if (!m_Rotation) m_Rotation = new G4RotationMatrix();
m_Rotation->set(G4ThreeVector(m(0,0), m(1,0), m(2,0)),
G4ThreeVector(m(0,1), m(1,1), m(2,1)),
G4ThreeVector(m(0,2), m(1,2), m(2,2)));
// 3. If object is already placed, update its transformation
if (m_Physical) {
m_Physical->SetTranslation(*m_Position);
m_Physical->SetRotation(m_Rotation);
}
}
void Solid::SetParent(Solid *parent) {
if (!m_Logical) {
std::cerr << "logical volume not created for solid " << GetName() << std::endl;
return;
}
if(m_Physical) {
std::cerr << "physical volume already created for solid " << GetName() << std::endl;
return;
}
G4LogicalVolume* parentLogical = nullptr;
if (parent) {
parentLogical = parent->GetLogical();
if (!parentLogical) {
std::cerr << "parent logical volume not created for solid " << parent->GetName() << std::endl;
return;
}
}
// G4PVPlacement
m_Physical = new G4PVPlacement(
m_Rotation, // Rotation
*m_Position, // Position (translation) inside the parent
m_Logical, // The logical volume of this solid (the child)
GetName(), // Name of the physical volume
parentLogical, // The logical volume of the parent (nullptr if it's the World volume)
false, // Boolean operations (usually false)
0, // Copy number
true // Check overlaps (useful to enable in debug phase)
);
}
TessellatedSolid::TessellatedSolid(const char *name)
: BaseClass(name), m_Solid(new G4TessellatedSolid(name)) {
}
void TessellatedSolid::SetMesh(TriangleMesh &mesh) {
G4TessellatedSolid *ts = this->m_Solid;
for (int i = 0; i < mesh.Triangles().size(); ++i) {
const Vector3i &trg = mesh.Triangles().at(i);
G4TriangularFacet *facet = new G4TriangularFacet(
DetectorsSolidImpl::getG4Vector3f(mesh.Points().at(trg(0))),
DetectorsSolidImpl::getG4Vector3f(mesh.Points().at(trg(1))),
DetectorsSolidImpl::getG4Vector3f(mesh.Points().at(trg(2))), ABSOLUTE);
ts->AddFacet((G4VFacet *)facet);
}
if (this->m_Logical) {
this->m_Logical->SetSolid(ts);
}
}
BoxSolid::BoxSolid(const char *name, ContainerBox *box) : BaseClass(name) {
m_Solid = new G4Box(name, 1,1,1);
m_Object = box;
Object::connect(box, &ContainerBox::Updated, this, &BoxSolid::Update);
if (m_Logical) {
m_Logical->SetSolid(m_Solid);
}
Update();
}
void BoxSolid::Update() {
if (m_Object) {
Vector3f size = m_Object->GetSize();
m_Solid->SetXHalfLength(size(0) * 0.5);
m_Solid->SetYHalfLength(size(1) * 0.5);
m_Solid->SetZHalfLength(size(2) * 0.5);
// Geant4 placement is relative to center. uLib Box is anchored at corner.
// 1. Get position and rotation (clean, without scale)
Vector3f pos = m_Object->GetPosition();
Matrix3f rot = m_Object->GetRotation();
// 2. Center = Corner + Rotation * (Half-Size)
// We must rotate the offset vector because uLib box can be rotated.
Vector3f center = pos + rot * (size * 0.5);
uLib::AffineTransform t;
t.SetPosition(center);
t.SetRotation(rot);
this->SetTransform(t.GetMatrix());
}
}
} // namespace Geant
} // namespace uLib

123
src/HEP/Geant/Solid.h Normal file
View File

@@ -0,0 +1,123 @@
/*//////////////////////////////////////////////////////////////////////////////
// CMT Cosmic Muon Tomography project //////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
Copyright (c) 2014, Universita' degli Studi di Padova, INFN sez. di Padova
All rights reserved
Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it >
------------------------------------------------------------------
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library.
//////////////////////////////////////////////////////////////////////////////*/
#ifndef SOLID_H
#define SOLID_H
#include "Core/Object.h"
#include "Geant/Matter.h"
#include <Geant4/G4LogicalVolume.hh>
#include "Math/ContainerBox.h"
#include "Math/Dense.h"
#include "Math/TriangleMesh.h"
class G4Material;
class G4LogicalVolume;
class G4TessellatedSolid;
class G4Box;
namespace uLib {
namespace Geant {
class Solid : public Object {
public:
Solid();
Solid(const char *name);
virtual ~Solid();
void SetNistMaterial(const char *name);
void SetMaterial(G4Material *material);
void SetSizeUnit(const char *unit);
// Implementiamo SetParent qui, per tutti.
virtual void SetParent(Solid *parent);
// Setters per la posizione (necessari per il piazzamento)
void SetTransform(Matrix4f transform);
uLibGetMacro(Material, G4Material *)
uLibGetSetMacro(Logical, G4LogicalVolume *)
uLibGetSetMacro(Physical, G4VPhysicalVolume *)
virtual G4VSolid* GetG4Solid() const { return nullptr; }
inline const char *GetName() const {
return m_Logical ? m_Logical->GetName().c_str() : m_Name.c_str();
}
protected:
std::string m_Name;
G4Material *m_Material;
G4LogicalVolume *m_Logical;
G4VPhysicalVolume *m_Physical; // <-- Memorizza l'oggetto posizionato
G4ThreeVector *m_Position; // <-- Offset rispetto al centro del padre
G4RotationMatrix* m_Rotation; // <-- Rotazione rispetto al padre
};
class TessellatedSolid : public Solid {
typedef Solid BaseClass;
public:
TessellatedSolid(const char *name);
void SetMesh(TriangleMesh &mesh);
uLibGetMacro(Solid, G4TessellatedSolid *)
virtual G4VSolid* GetG4Solid() const override { return (G4VSolid*)m_Solid; }
public slots:
void Update();
private :
G4TessellatedSolid *m_Solid;
};
class BoxSolid : public Solid {
typedef Solid BaseClass;
public:
BoxSolid(const char *name, ContainerBox *box);
virtual G4VSolid* GetG4Solid() const override { return (G4VSolid*)m_Solid; }
ContainerBox* GetObject() const { return m_Object; }
public slots:
void Update();
private:
ContainerBox *m_Object;
G4Box *m_Solid;
};
} // namespace Geant
} // namespace uLib
#endif // SOLID_H

View File

@@ -0,0 +1,125 @@
#include "SteppingAction.hh"
#include <Geant4/G4Step.hh>
#include <Geant4/G4Track.hh>
#include <Geant4/G4Event.hh>
#include <Geant4/G4SystemOfUnits.hh>
#include <set>
#include <iostream>
#include <mutex>
#include <cmath>
namespace uLib {
namespace Geant {
SteppingAction::SteppingAction(SimulationContext *context)
: G4UserSteppingAction(),
G4UserEventAction(),
m_Context(context),
m_Verbosity(0)
{}
SteppingAction::~SteppingAction() {}
void SteppingAction::BeginOfEventAction(const G4Event *event) {
if (!event || !m_Context) return;
if (m_Context->mode == SimulationMode::DETAILED) {
m_CurrentGeant = GeantEvent();
m_CurrentGeant.m_EventID = static_cast<Id_t>(event->GetEventID());
if (event->GetNumberOfPrimaryVertex() > 0) {
G4PrimaryVertex *vtx = event->GetPrimaryVertex(0);
G4ThreeVector pos = vtx->GetPosition();
m_CurrentGeant.m_GenVector.origin = HPoint3f(pos.x(), pos.y(), pos.z());
if (vtx->GetNumberOfParticle() > 0) {
G4PrimaryParticle *prim = vtx->GetPrimary(0);
G4ThreeVector mom = prim->GetMomentumDirection();
m_CurrentGeant.m_GenVector.direction = HVector3f(mom.x(), mom.y(), mom.z());
m_CurrentGeant.m_Momentum = static_cast<Scalarf>(prim->GetTotalMomentum() / MeV);
}
}
} else {
// Detector mode
m_MuonCrossCount = 0;
float nan = std::numeric_limits<float>::quiet_NaN();
m_CurrentMuon.LineIn().origin = HPoint3f(nan, nan, nan);
m_CurrentMuon.LineIn().direction = HVector3f(nan, nan, nan);
m_CurrentMuon.LineOut().origin = HPoint3f(nan, nan, nan);
m_CurrentMuon.LineOut().direction = HVector3f(nan, nan, nan);
m_CurrentMuon.Momentum() = nan;
}
}
void SteppingAction::EndOfEventAction(const G4Event *event) {
if (!m_Context) return;
if (m_Context->mode == SimulationMode::DETAILED && m_Context->outputGeant) {
if (!m_CurrentGeant.m_Path.empty()) {
std::lock_guard<std::mutex> lock(m_Context->outputMutex);
m_Context->outputGeant->push_back(m_CurrentGeant);
}
} else if (m_Context->mode == SimulationMode::DETECTOR && m_Context->outputMuon) {
// In detector mode, we always push the event (to keep indexing consistent)
// or only if hit? User requested "all muon event line in and out ar at first set to nan".
// So we push everything.
std::lock_guard<std::mutex> lock(m_Context->outputMutex);
m_Context->outputMuon->push_back(m_CurrentMuon);
}
}
void SteppingAction::UserSteppingAction(const G4Step *step) {
if (!step || !m_Context) return;
const G4Track *track = step->GetTrack();
if (!track || track->GetParentID() != 0) return;
if (m_Context->mode == SimulationMode::DETAILED) {
GeantEvent::Delta delta;
delta.m_Length = static_cast<Scalarf>(step->GetStepLength() / mm);
delta.m_Momentum = static_cast<Scalarf>(track->GetMomentum().mag() / MeV);
G4ThreeVector dir = track->GetMomentumDirection();
delta.m_Direction = HVector3f(dir.x(), dir.y(), dir.z());
if (track->GetVolume()) {
delta.m_SolidName = track->GetVolume()->GetName();
}
m_CurrentGeant.m_Path.push_back(delta);
} else {
// Detector Mode
if (std::isnan(m_CurrentMuon.Momentum())) {
m_CurrentMuon.Momentum() = static_cast<Scalarf>(track->GetMomentum().mag() / MeV);
}
G4ThreeVector p1 = step->GetPreStepPoint()->GetPosition();
G4ThreeVector p2 = step->GetPostStepPoint()->GetPosition();
G4ThreeVector dir_g4 = track->GetMomentumDirection();
HPoint3f p1f(p1.x(), p1.y(), p1.z());
HPoint3f p2f(p2.x(), p2.y(), p2.z());
HVector3f dirf(dir_g4.x(), dir_g4.y(), dir_g4.z());
for (const auto& plane : m_Context->detectorPlanes) {
float d1 = (p1f - plane.origin).dot(plane.direction);
float d2 = (p2f - plane.origin).dot(plane.direction);
if ((d1 > 0 && d2 <= 0) || (d1 < 0 && d2 >= 0)) {
float t = d1 / (d1 - d2);
HPoint3f intersection = p1f + t * (p2f - p1f);
if (m_MuonCrossCount == 0) {
m_CurrentMuon.LineIn().origin = intersection;
m_CurrentMuon.LineIn().direction = dirf;
m_MuonCrossCount++;
} else if (m_MuonCrossCount == 1) {
m_CurrentMuon.LineOut().origin = intersection;
m_CurrentMuon.LineOut().direction = dirf;
m_MuonCrossCount++;
}
}
}
}
}
} // namespace Geant
} // namespace uLib

View File

@@ -0,0 +1,37 @@
#ifndef U_GEANT_STEPPINGACTION_HH
#define U_GEANT_STEPPINGACTION_HH
#include "G4UserSteppingAction.hh"
#include "G4UserEventAction.hh"
#include "Core/Vector.h"
#include "GeantEvent.h"
#include "HEP/Detectors/MuonEvent.h"
#include "SimulationContext.h"
namespace uLib {
namespace Geant {
/// SteppingAction collects scattering data at each Geant4 step.
class SteppingAction : public G4UserSteppingAction, public G4UserEventAction {
public:
SteppingAction(SimulationContext *context);
virtual ~SteppingAction();
virtual void UserSteppingAction(const G4Step *step) override;
virtual void BeginOfEventAction(const G4Event *event) override;
virtual void EndOfEventAction(const G4Event *event) override;
void SetVerbosity(int level) { m_Verbosity = level; }
private:
SimulationContext *m_Context;
GeantEvent m_CurrentGeant;
MuonEvent m_CurrentMuon;
int m_MuonCrossCount = 0;
int m_Verbosity = 0;
};
} // namespace Geant
} // namespace uLib
#endif

View File

@@ -0,0 +1,23 @@
#include "HEP/Geant/ActionInitialization.hh" // Il file appena creato
#include "G4RunManagerFactory.hh" // Per il RunManager moderno
// ... altri include (DetectorConstruction, PhysicsList, ecc.)
int main(int argc, char **argv) {
// Creazione del Run Manager
auto *runManager = G4RunManagerFactory::CreateRunManager();
// 1. Inizializzazione della Geometria
// runManager->SetUserInitialization(new DetectorConstruction());
// 2. Inizializzazione della Fisica
// runManager->SetUserInitialization(new PhysicsList());
// 3. INIZIALIZZAZIONE DELLE AZIONI (Il nostro generatore!)
runManager->SetUserInitialization(new uLib::Geant::ActionInitialization(nullptr, nullptr));
// ... Inizializzazione del kernel ( runManager->Initialize(); ), UI manager,
// vis manager, ecc.
delete runManager;
return 0;
}

View File

@@ -0,0 +1,16 @@
# TESTS
set(TESTS
SolidTest
EventTest
GeantApp
ActionInitialization
SkyPlaneEmitterTest
)
set(LIBRARIES
${PACKAGE_LIBPREFIX}Core
${PACKAGE_LIBPREFIX}Math
${PACKAGE_LIBPREFIX}Geant
Eigen3::Eigen
)
uLib_add_tests(Geant)

View File

@@ -0,0 +1,90 @@
#include "Geant/Solid.h"
#include "HEP/Geant/GeantEvent.h"
#include "HEP/Geant/Scene.h"
#include "HEP/Geant/EmitterPrimary.hh"
#include "Math/ContainerBox.h"
#include "Math/TriangleMesh.h"
#include "Math/Dense.h"
#include "Math/Units.h"
#include "testing-prototype.h"
#include <Geant4/G4Material.hh>
#include <Geant4/G4NistManager.hh>
#include <Geant4/G4LogicalVolume.hh>
#include <Geant4/G4TessellatedSolid.hh>
#include <string.h>
using namespace uLib;
int main() {
BEGIN_TESTING(Geant Event);
// Test: Scene with iron cube in air, launch muons, collect events //
{
// 1. Create world box (air, 30m x 30m x 30m)
Geant::Scene scene;
scene.ConstructWorldBox(Vector3f(30_m, 30_m, 30_m), "G4_AIR");
// 2. Create iron cube (1m x 1m x 1m) at center
ContainerBox iron_box(Vector3f(1000, 1000, 1000)); // mm
Geant::BoxSolid *iron_cube = new Geant::BoxSolid("IronCube", &iron_box);
iron_cube->SetNistMaterial("G4_Fe");
iron_cube->Update(); // apply dimensions
scene.AddSolid(iron_cube);
// 3. Set up emitter (default: mu- at 1 GeV, from z=+10m downward)
Geant::EmitterPrimary *emitter = new Geant::EmitterPrimary();
scene.SetEmitter(emitter);
// 4. Initialize Geant4
scene.Initialize();
// 5. Run simulation: 10 muons
int nEvents = 10;
Vector<Geant::GeantEvent> results;
scene.RunSimulation(nEvents, results);
// 6. Check results
printf(" Collected %zu events\n", results.size());
TEST1(results.size() > 0);
for (size_t i = 0; i < results.size(); ++i) {
const Geant::GeantEvent &ev = results[i];
bool hitIron = false;
for (const auto &d : ev.Path()) {
if (d.SolidName() == "IronCube") {
hitIron = true;
break;
}
}
printf(" Event %d: momentum=%.1f MeV, path steps=%zu, hitIron=%s\n",
ev.GetEventID(),
ev.GetMomentum(),
ev.Path().size(),
hitIron ? "YES" : "NO");
// Each event should have at least one step
TEST1(ev.Path().size() > 0);
// Print first few deltas
const auto &path = ev.Path();
for (size_t j = 0; j < path.size() && j < 5; ++j) {
const auto &d = path[j];
printf(" Delta[%zu]: solid=%s len=%.2f mm, p=%.1f MeV, "
"dir=(%.3f, %.3f, %.3f)\n",
j,
d.SolidName().c_str(),
d.GetLength(),
d.GetMomentum(),
d.Direction()(0),
d.Direction()(1),
d.Direction()(2));
}
if (path.size() > 5) {
printf(" ... (%zu more deltas)\n", path.size() - 5);
}
}
}
END_TESTING
}

View File

@@ -0,0 +1,16 @@
#include "Math/ContainerBox.h"
#include "Math/Dense.h"
#include "HEP/Geant/Scene.h"
using namespace uLib;
int main() {
uLib::Geant::Scene scene;
scene.ConstructWorldBox(Vector3f(100, 100, 100), "G4_AIR");
scene.Initialize();
return 0;
}

View File

@@ -0,0 +1,104 @@
#include "Geant/Solid.h"
#include "HEP/Geant/GeantEvent.h"
#include "HEP/Geant/Scene.h"
#include "HEP/Geant/EmitterPrimary.hh"
#include "Math/ContainerBox.h"
#include "Math/Dense.h"
#include "Math/Units.h"
#include "HEP/Detectors/DetectorChamber.h"
#include <Geant4/G4SystemOfUnits.hh>
#include <iostream>
using namespace uLib;
int main(int argc, char** argv) {
int nEvents = 10000;
if (argc > 1) {
nEvents = std::stoi(argv[1]);
}
// 1. Setup Geant4 Scene
Geant::Scene scene;
scene.ConstructWorldBox(Vector3f(30_m, 30_m, 30_m), "G4_AIR");
ContainerBox iron_box;
iron_box.Scale(Vector3f(18_m, 10_cm, 18_m));
iron_box.SetPosition(Vector3f(-9_m, -5_cm, -9_m));
Geant::BoxSolid* iron_cube = new Geant::BoxSolid("IronCube", &iron_box);
iron_cube->SetNistMaterial("G4_Fe");
iron_cube->Update();
scene.AddSolid(iron_cube);
// Top Detector Chamber (along Y axis)
DetectorChamber* top_chamber_box = new DetectorChamber();
top_chamber_box->Scale(Vector3f(20_m, 40_cm, 20_m));
top_chamber_box->Rotate(90_deg, Vector3f(1, 0, 0));
top_chamber_box->SetPosition(Vector3f(-10_m, 12_m, -10_m));
Geant::BoxSolid* top_chamber = new Geant::BoxSolid("TopChamber", top_chamber_box);
top_chamber->SetNistMaterial("G4_AIR");
top_chamber->Update();
scene.AddSolid(top_chamber);
// Bottom Detector Chamber (along Y axis)
DetectorChamber* bottom_chamber_box = new DetectorChamber();
bottom_chamber_box->Scale(Vector3f(20_m, 40_cm, 20_m));
bottom_chamber_box->Rotate(90_deg, Vector3f(1, 0, 0));
bottom_chamber_box->SetPosition(Vector3f(-10_m, -12_m, -10_m));
Geant::BoxSolid* bottom_chamber = new Geant::BoxSolid("BottomChamber", bottom_chamber_box);
bottom_chamber->SetNistMaterial("G4_AIR");
bottom_chamber->Update();
scene.AddSolid(bottom_chamber);
// Setup SkyPlaneEmitterPrimary
Geant::SkyPlaneEmitterPrimary* emitter = new Geant::SkyPlaneEmitterPrimary();
emitter->SetPosition(Vector3f(0, 14.9_m, 0));
emitter->Rotate(-90_deg, Vector3f(1, 0, 0));
emitter->SetSkySize(Vector2f(20_m, 20_m));
scene.SetEmitter(emitter);
scene.SetVerbosity(1);
// scene.Initialize(); // Removed to avoid premature initialization
std::cout << "Starting simulation of " << nEvents << " events..." << std::endl;
Vector<Geant::GeantEvent> results;
scene.RunSimulation(nEvents, results);
std::cout << "Simulation finished. Collected " << results.size() << " events." << std::endl;
// Sample output to verify data collection
if (!results.empty()) {
std::cout << "Summary: " << std::endl;
std::cout << " Total events generated: " << results.size() << std::endl;
size_t total_steps = 0;
for (const auto& event : results) {
total_steps += event.Path().size();
}
std::cout << " Total simulation steps: " << total_steps << std::endl;
std::cout << " Average steps per event: " << static_cast<double>(total_steps) / results.size() << std::endl;
}
std::cout << "\nStarting Detector Simulation of " << nEvents << " events..." << std::endl;
Vector<MuonEvent> detectorResults;
scene.RunDetectorSimulation(nEvents, detectorResults);
std::cout << "Detector Simulation finished." << std::endl;
size_t hit_count = 0;
for (const auto& ev : detectorResults) {
if (!std::isnan(ev.LineIn().origin.x())) {
hit_count++;
}
}
std::cout << " Muons crossing at least one detector: " << hit_count << std::endl;
if (nEvents > 0) {
std::cout << " Efficiency: " << (100.0 * hit_count / nEvents) << "%" << std::endl;
}
return 0;
}

View File

@@ -0,0 +1,73 @@
#include "Geant/Solid.h"
#include "Math/TriangleMesh.h"
#include "testing-prototype.h"
#include <Geant4/G4Material.hh>
#include <Geant4/G4NistManager.hh>
#include <Geant4/G4LogicalVolume.hh>
#include <Geant4/G4TessellatedSolid.hh>
#include <string.h>
using namespace uLib;
int main() {
BEGIN_TESTING(Geant Solid);
// Test Solid initialization and NIST material //
{
Geant::Solid solid("test_solid");
// Logical volume is not created until material and solid are set
TEST1(solid.GetLogical() == nullptr);
solid.SetNistMaterial("G4_AIR");
// Still null because base Solid has no GetG4Solid()
TEST1(solid.GetLogical() == nullptr);
TEST1(solid.GetMaterial() != nullptr);
TEST1(solid.GetMaterial()->GetName() == "G4_AIR");
}
// Test TessellatedSolid with a simple mesh //
{
Geant::TessellatedSolid tsolid("test_tessellated");
tsolid.SetNistMaterial("G4_AIR");
TEST1(tsolid.GetLogical() != nullptr);
TEST1(tsolid.GetSolid() != nullptr);
// cube mesh //
TriangleMesh mesh;
mesh.AddPoint(Vector3f(0,0,0));
mesh.AddPoint(Vector3f(1,0,0));
mesh.AddPoint(Vector3f(0,1,0));
mesh.AddPoint(Vector3f(1,1,0));
mesh.AddPoint(Vector3f(0,0,1));
mesh.AddPoint(Vector3f(1,0,1));
mesh.AddPoint(Vector3f(0,1,1));
mesh.AddPoint(Vector3f(1,1,1));
// create triangles (consistent outward winding) //
// bottom (z=0)
mesh.AddTriangle(Vector3i(0,2,3));
mesh.AddTriangle(Vector3i(0,3,1));
// top (z=1)
mesh.AddTriangle(Vector3i(4,5,7));
mesh.AddTriangle(Vector3i(4,7,6));
// left (x=0)
mesh.AddTriangle(Vector3i(0,4,6));
mesh.AddTriangle(Vector3i(0,6,2));
// right (x=1)
mesh.AddTriangle(Vector3i(1,3,7));
mesh.AddTriangle(Vector3i(1,7,5));
// front (y=0)
mesh.AddTriangle(Vector3i(0,1,5));
mesh.AddTriangle(Vector3i(0,5,4));
// back (y=1)
mesh.AddTriangle(Vector3i(2,6,7));
mesh.AddTriangle(Vector3i(2,7,3));
tsolid.SetMesh(mesh);
TEST1(tsolid.GetSolid()->GetNumberOfFacets() == 12);
}
END_TESTING
}

View File

@@ -25,24 +25,13 @@
#ifndef U_CHAMBERDETECTOR_H
#define U_CHAMBERDETECTOR_H
#include <stdio.h>
#include "Core/Types.h"
#include "Math/ContainerBox.h"
#define BEGIN_TESTING(name) \
static int _fail = 0; \
printf("..:: Testing " #name " ::..\n");
namespace uLib {
#define TEST1(val) _fail += (val)==0
#define TEST0(val) _fail += (val)!=0
#define END_TESTING return _fail;
class DetectorChamber : public ContainerBox {
public:
private:
};
}
#endif // CHAMBERDETECTOR_H

View File

@@ -39,12 +39,6 @@ class Hit {
Type m_DriftTime;
};
class HitMC {
public:
virtual Id_t GetChamber() = 0;

373
src/Math/CLHEP_defs.hh Normal file
View File

@@ -0,0 +1,373 @@
#if !defined(HAVE_GEANT4) && !defined(HEP_SYSTEM_OF_UNITS_H) && !defined(HEP_PHYSICAL_CONSTANTS_H)
#ifndef CLHEP_defs_h
#define CLHEP_defs_h
namespace CLHEP {
// UNITS //
//
//
//
static constexpr double pi = 3.14159265358979323846;
static constexpr double twopi = 2*pi;
static constexpr double halfpi = pi/2;
static constexpr double pi2 = pi*pi;
//
//
//
static constexpr double millimeter = 1.;
static constexpr double millimeter2 = millimeter*millimeter;
static constexpr double millimeter3 = millimeter*millimeter*millimeter;
static constexpr double centimeter = 10.*millimeter;
static constexpr double centimeter2 = centimeter*centimeter;
static constexpr double centimeter3 = centimeter*centimeter*centimeter;
static constexpr double meter = 1000.*millimeter;
static constexpr double meter2 = meter*meter;
static constexpr double meter3 = meter*meter*meter;
static constexpr double kilometer = 1000.*meter;
static constexpr double kilometer2 = kilometer*kilometer;
static constexpr double kilometer3 = kilometer*kilometer*kilometer;
static constexpr double parsec = 3.0856775807e+16*meter;
static constexpr double micrometer = 1.e-6 *meter;
static constexpr double nanometer = 1.e-9 *meter;
static constexpr double angstrom = 1.e-10*meter;
static constexpr double fermi = 1.e-15*meter;
static constexpr double barn = 1.e-28*meter2;
static constexpr double millibarn = 1.e-3 *barn;
static constexpr double microbarn = 1.e-6 *barn;
static constexpr double nanobarn = 1.e-9 *barn;
static constexpr double picobarn = 1.e-12*barn;
// symbols
static constexpr double nm = nanometer;
static constexpr double um = micrometer;
static constexpr double mm = millimeter;
static constexpr double mm2 = millimeter2;
static constexpr double mm3 = millimeter3;
static constexpr double cm = centimeter;
static constexpr double cm2 = centimeter2;
static constexpr double cm3 = centimeter3;
static constexpr double liter = 1.e+3*cm3;
static constexpr double L = liter;
static constexpr double dL = 1.e-1*liter;
static constexpr double cL = 1.e-2*liter;
static constexpr double mL = 1.e-3*liter;
static constexpr double m = meter;
static constexpr double m2 = meter2;
static constexpr double m3 = meter3;
static constexpr double km = kilometer;
static constexpr double km2 = kilometer2;
static constexpr double km3 = kilometer3;
static constexpr double pc = parsec;
//
// Angle
//
static constexpr double radian = 1.;
static constexpr double milliradian = 1.e-3*radian;
static constexpr double degree = (pi/180.0)*radian;
static constexpr double steradian = 1.;
// symbols
static constexpr double rad = radian;
static constexpr double mrad = milliradian;
static constexpr double sr = steradian;
static constexpr double deg = degree;
//
// Time [T]
//
static constexpr double nanosecond = 1.;
static constexpr double second = 1.e+9 *nanosecond;
static constexpr double millisecond = 1.e-3 *second;
static constexpr double microsecond = 1.e-6 *second;
static constexpr double picosecond = 1.e-12*second;
static constexpr double minute = 60*second;
static constexpr double hour = 60*minute;
static constexpr double day = 24*hour;
static constexpr double year = 365*day;
static constexpr double hertz = 1./second;
static constexpr double kilohertz = 1.e+3*hertz;
static constexpr double megahertz = 1.e+6*hertz;
// symbols
static constexpr double ns = nanosecond;
static constexpr double s = second;
static constexpr double ms = millisecond;
static constexpr double us = microsecond;
static constexpr double ps = picosecond;
//
// Electric charge [Q]
//
static constexpr double eplus = 1. ;// positron charge
static constexpr double e_SI = 1.602176634e-19;// positron charge in coulomb
static constexpr double coulomb = eplus/e_SI;// coulomb = 6.24150 e+18 * eplus
//
// Energy [E]
//
static constexpr double megaelectronvolt = 1. ;
static constexpr double electronvolt = 1.e-6*megaelectronvolt;
static constexpr double kiloelectronvolt = 1.e-3*megaelectronvolt;
static constexpr double gigaelectronvolt = 1.e+3*megaelectronvolt;
static constexpr double teraelectronvolt = 1.e+6*megaelectronvolt;
static constexpr double petaelectronvolt = 1.e+9*megaelectronvolt;
static constexpr double millielectronvolt = 1.e-9*megaelectronvolt;
static constexpr double joule = electronvolt/e_SI;// joule = 6.24150 e+12 * MeV
// symbols
static constexpr double MeV = megaelectronvolt;
static constexpr double eV = electronvolt;
static constexpr double keV = kiloelectronvolt;
static constexpr double GeV = gigaelectronvolt;
static constexpr double TeV = teraelectronvolt;
static constexpr double PeV = petaelectronvolt;
//
// Mass [E][T^2][L^-2]
//
static constexpr double kilogram = joule*second*second/(meter*meter);
static constexpr double gram = 1.e-3*kilogram;
static constexpr double milligram = 1.e-3*gram;
// symbols
static constexpr double kg = kilogram;
static constexpr double g = gram;
static constexpr double mg = milligram;
//
// Power [E][T^-1]
//
static constexpr double watt = joule/second;// watt = 6.24150 e+3 * MeV/ns
//
// Force [E][L^-1]
//
static constexpr double newton = joule/meter;// newton = 6.24150 e+9 * MeV/mm
//
// Pressure [E][L^-3]
//
#define pascal hep_pascal // a trick to avoid warnings
static constexpr double hep_pascal = newton/m2; // pascal = 6.24150 e+3 * MeV/mm3
static constexpr double bar = 100000*pascal; // bar = 6.24150 e+8 * MeV/mm3
static constexpr double atmosphere = 101325*pascal; // atm = 6.32420 e+8 * MeV/mm3
//
// Electric current [Q][T^-1]
//
static constexpr double ampere = coulomb/second; // ampere = 6.24150 e+9 * eplus/ns
static constexpr double milliampere = 1.e-3*ampere;
static constexpr double microampere = 1.e-6*ampere;
static constexpr double nanoampere = 1.e-9*ampere;
//
// Electric potential [E][Q^-1]
//
static constexpr double megavolt = megaelectronvolt/eplus;
static constexpr double kilovolt = 1.e-3*megavolt;
static constexpr double volt = 1.e-6*megavolt;
//
// Electric resistance [E][T][Q^-2]
//
static constexpr double ohm = volt/ampere;// ohm = 1.60217e-16*(MeV/eplus)/(eplus/ns)
//
// Electric capacitance [Q^2][E^-1]
//
static constexpr double farad = coulomb/volt;// farad = 6.24150e+24 * eplus/Megavolt
static constexpr double millifarad = 1.e-3*farad;
static constexpr double microfarad = 1.e-6*farad;
static constexpr double nanofarad = 1.e-9*farad;
static constexpr double picofarad = 1.e-12*farad;
//
// Magnetic Flux [T][E][Q^-1]
//
static constexpr double weber = volt*second;// weber = 1000*megavolt*ns
//
// Magnetic Field [T][E][Q^-1][L^-2]
//
static constexpr double tesla = volt*second/meter2;// tesla =0.001*megavolt*ns/mm2
static constexpr double gauss = 1.e-4*tesla;
static constexpr double kilogauss = 1.e-1*tesla;
//
// Inductance [T^2][E][Q^-2]
//
static constexpr double henry = weber/ampere;// henry = 1.60217e-7*MeV*(ns/eplus)**2
//
// Temperature
//
static constexpr double kelvin = 1.;
//
// Amount of substance
//
static constexpr double mole = 1.;
//
// Activity [T^-1]
//
static constexpr double becquerel = 1./second ;
static constexpr double curie = 3.7e+10 * becquerel;
static constexpr double kilobecquerel = 1.e+3*becquerel;
static constexpr double megabecquerel = 1.e+6*becquerel;
static constexpr double gigabecquerel = 1.e+9*becquerel;
static constexpr double millicurie = 1.e-3*curie;
static constexpr double microcurie = 1.e-6*curie;
static constexpr double Bq = becquerel;
static constexpr double kBq = kilobecquerel;
static constexpr double MBq = megabecquerel;
static constexpr double GBq = gigabecquerel;
static constexpr double Ci = curie;
static constexpr double mCi = millicurie;
static constexpr double uCi = microcurie;
//
// Absorbed dose [L^2][T^-2]
//
static constexpr double gray = joule/kilogram ;
static constexpr double kilogray = 1.e+3*gray;
static constexpr double milligray = 1.e-3*gray;
static constexpr double microgray = 1.e-6*gray;
//
// Luminous intensity [I]
//
static constexpr double candela = 1.;
//
// Luminous flux [I]
//
static constexpr double lumen = candela*steradian;
//
// Illuminance [I][L^-2]
//
static constexpr double lux = lumen/meter2;
//
// Miscellaneous
//
static constexpr double perCent = 0.01 ;
static constexpr double perThousand = 0.001;
static constexpr double perMillion = 0.000001;
// CONSTANTS //
//
//
//
static constexpr double Avogadro = 6.02214076e+23/mole;
//
// c = 299.792458 mm/ns
// c^2 = 898.7404 (mm/ns)^2
//
static constexpr double c_light = 2.99792458e+8 * m/s;
static constexpr double c_squared = c_light * c_light;
//
// h = 4.13566e-12 MeV*ns
// hbar = 6.58212e-13 MeV*ns
// hbarc = 197.32705e-12 MeV*mm
//
static constexpr double h_Planck = 6.62607015e-34 * joule*s;
static constexpr double hbar_Planck = h_Planck/twopi;
static constexpr double hbarc = hbar_Planck * c_light;
static constexpr double hbarc_squared = hbarc * hbarc;
//
//
//
static constexpr double electron_charge = - eplus; // see SystemOfUnits.h
static constexpr double e_squared = eplus * eplus;
//
// amu_c2 - atomic equivalent mass unit
// - AKA, unified atomic mass unit (u)
// amu - atomic mass unit
//
static constexpr double electron_mass_c2 = 0.510998910 * MeV;
static constexpr double proton_mass_c2 = 938.272013 * MeV;
static constexpr double neutron_mass_c2 = 939.56536 * MeV;
static constexpr double amu_c2 = 931.494028 * MeV;
static constexpr double amu = amu_c2/c_squared;
//
// permeability of free space mu0 = 2.01334e-16 Mev*(ns*eplus)^2/mm
// permittivity of free space epsil0 = 5.52636e+10 eplus^2/(MeV*mm)
//
static constexpr double mu0 = 4*pi*1.e-7 * henry/m;
static constexpr double epsilon0 = 1./(c_squared*mu0);
//
// electromagnetic coupling = 1.43996e-12 MeV*mm/(eplus^2)
//
static constexpr double elm_coupling = e_squared/(4*pi*epsilon0);
static constexpr double fine_structure_const = elm_coupling/hbarc;
static constexpr double classic_electr_radius = elm_coupling/electron_mass_c2;
static constexpr double electron_Compton_length = hbarc/electron_mass_c2;
static constexpr double Bohr_radius = electron_Compton_length/fine_structure_const;
static constexpr double alpha_rcl2 = fine_structure_const
*classic_electr_radius
*classic_electr_radius;
static constexpr double twopi_mc2_rcl2 = twopi*electron_mass_c2
*classic_electr_radius
*classic_electr_radius;
static constexpr double Bohr_magneton = (eplus*hbarc*c_light)/(2*electron_mass_c2);
static constexpr double nuclear_magneton = (eplus*hbarc*c_light)/(2*proton_mass_c2);
//
//
//
static constexpr double k_Boltzmann = 8.617333e-11 * MeV/kelvin;
//
//
//
static constexpr double STP_Temperature = 273.15*kelvin;
static constexpr double STP_Pressure = 1.*atmosphere;
static constexpr double kGasThreshold = 10.*mg/cm3;
//
//
//
static constexpr double universe_mean_density = 1.e-25*g/cm3;
} // namespace CLHEP
#endif // CLHEP_defs_h
#endif // HAVE_GEANT4 / CLHEP checks

View File

@@ -19,20 +19,25 @@ set(HEADERS ContainerBox.h
VoxImageFilterCustom.hpp
Accumulator.h
TriangleMesh.h
QuadMesh.h
BitCode.h
Structured2DGrid.h
Structured4DGrid.h)
Structured4DGrid.h
Units.h
CLHEP_defs.hh)
set(SOURCES VoxRaytracer.cpp
StructuredData.cpp
StructuredGrid.cpp
VoxImage.cpp
TriangleMesh.cpp
QuadMesh.cpp
Dense.cpp
Structured2DGrid.cpp
Structured4DGrid.cpp)
set(LIBRARIES Eigen3::Eigen
set(LIBRARIES ${PACKAGE_LIBPREFIX}Core
Eigen3::Eigen
${ROOT_LIBRARIES}
${VTK_LIBRARIES})
@@ -54,7 +59,7 @@ endif()
install(TARGETS ${libname}
EXPORT "${PROJECT_NAME}Targets"
EXPORT "uLibTargets"
RUNTIME DESTINATION ${INSTALL_BIN_DIR} COMPONENT bin
LIBRARY DESTINATION ${INSTALL_LIB_DIR} COMPONENT lib)

View File

@@ -23,76 +23,169 @@
//////////////////////////////////////////////////////////////////////////////*/
#ifndef U_CONTAINERBOX_H
#define U_CONTAINERBOX_H
#include "Geometry.h"
#include "Core/Object.h"
#include "Math/Dense.h"
#include "Math/Transform.h"
#include <utility>
namespace uLib {
/**
* @brief Represents an oriented bounding box (OBB) within a hierarchical
* transformation system.
*
* ContainerBox inherits from AffineTransform, which defines its parent
* coordinate system. It contains an internal local transformation (m_LocalT)
* that defines the box's specific origin and size relative to its own
* coordinate system.
*/
class ContainerBox : public AffineTransform, public Object {
typedef AffineTransform BaseClass;
class ContainerBox : public AffineTransform {
public:
ContainerBox() : m_LocalT(this) {}
/**
* @brief Default constructor.
* Initializes the local transformation with this instance as its parent.
*/
ContainerBox()
: m_LocalT(this) // BaseClass is Parent of m_LocalTransform
{}
ContainerBox(const ContainerBox &copy) :
m_LocalT(this),
AffineTransform(copy)
{
// FIX for performance //
this->SetOrigin(copy.GetOrigin());
this->SetSize(copy.GetSize());
}
/**
* @brief Constructor with size.
* @param size The size vector.
*/
ContainerBox(const Vector3f &size) : m_LocalT(this) { this->SetSize(size); }
inline void SetOrigin(const Vector3f &v) { m_LocalT.SetPosition(v); }
/**
* @brief Copy constructor.
* @param copy The ContainerBox instance to copy from.
*/
ContainerBox(const ContainerBox &copy)
: m_LocalT(this), // BaseClass is Parent of m_LocalTransform
AffineTransform(copy) {
this->SetOrigin(copy.GetOrigin());
this->SetSize(copy.GetSize());
}
inline Vector3f GetOrigin() const { return m_LocalT.GetPosition(); }
/**
* @brief Sets the box origin relative to its coordinate system.
* @param v The origin position vector.
*/
inline void SetOrigin(const Vector3f &v) { m_LocalT.SetPosition(v); }
void SetSize(const Vector3f &v) {
Vector3f pos = this->GetOrigin();
m_LocalT = AffineTransform(this);
m_LocalT.Scale(v);
m_LocalT.SetPosition(pos);
}
/**
* @brief Gets the box origin relative to its coordinate system.
* @return The origin position vector.
*/
inline Vector3f GetOrigin() const { return m_LocalT.GetPosition(); }
inline Vector3f GetSize() const { return m_LocalT.GetScale(); }
/**
* @brief Sets the size of the box.
* Re-initializes the local transformation and applies the new scale.
* @param v The size vector (width, height, depth).
*/
void SetSize(const Vector3f &v) {
Vector3f pos = this->GetOrigin();
m_LocalT = AffineTransform(this); // regenerate local transform
m_LocalT.Scale(v);
m_LocalT.SetPosition(pos);
}
// FIX... //
inline void FlipLocalAxes(int first, int second)
{ m_LocalT.FlipAxes(first,second); }
/**
* @brief Gets the current size (scale) of the box.
* @return The size vector.
*/
inline Vector3f GetSize() const {
Vector3f s = this->GetScale();
Vector3f ls = m_LocalT.GetScale();
return Vector3f(s(0) * ls(0), s(1) * ls(1), s(2) * ls(2));
}
Matrix4f GetWorldMatrix() const { return m_LocalT.GetWorldMatrix(); }
/**
* @brief Swaps two local axes of the box.
* @param first Index of the first axis (0=X, 1=Y, 2=Z).
* @param second Index of the second axis (0=X, 1=Y, 2=Z).
*/
inline void FlipLocalAxes(int first, int second) {
m_LocalT.FlipAxes(first, second);
}
inline Vector4f GetWorldPoint(const Vector4f &v) const {
return m_LocalT.GetWorldMatrix() * v;
}
/**
* @brief Returns the world transformation matrix of the box's volume.
* @return A 4x4 transformation matrix.
*/
Matrix4f GetWorldMatrix() const { return m_LocalT.GetWorldMatrix(); }
inline Vector4f GetWorldPoint(const float x, const float y, const float z) {
return this->GetWorldPoint(Vector4f(x,y,z,1));
}
/**
* @brief Returns the local transformation matrix of the box's volume.
* @return A 4x4 transformation matrix.
*/
Matrix4f GetLocalMatrix() const { return m_LocalT.GetMatrix(); }
inline Vector4f GetLocalPoint(const Vector4f &v) const {
return m_LocalT.GetWorldMatrix().inverse() * v;
}
/**
* @brief Transforms a point from box-local space to world space.
* @param v The local point (4D homogeneous vector).
* @return The transformed point in world space.
*/
inline Vector4f GetWorldPoint(const Vector4f &v) const {
return m_LocalT.GetWorldMatrix() * v;
}
inline Vector4f GetLocalPoint(const float x, const float y, const float z) {
return this->GetLocalPoint(Vector4f(x,y,z,1));
}
/**
* @brief Transforms a point from box-local space coordinates to world space.
* @param x X coordinate in local space.
* @param y Y coordinate in local space.
* @param z Z coordinate in local space.
* @return The transformed point in world space.
*/
inline Vector4f GetWorldPoint(const float x, const float y, const float z) {
return this->GetWorldPoint(Vector4f(x, y, z, 1));
}
/**
* @brief Transforms a point from world space to box-local space.
* @param v The world point (4D homogeneous vector).
* @return The transformed point in box-local space.
*/
inline Vector4f GetLocalPoint(const Vector4f &v) const {
return m_LocalT.GetWorldMatrix().inverse() * v;
}
protected:
/**
* @brief Transforms a point from world space coordinates to box-local space.
* @param x X coordinate in world space.
* @param y Y coordinate in world space.
* @param z Z coordinate in world space.
* @return The transformed point in box-local space.
*/
inline Vector4f GetLocalPoint(const float x, const float y, const float z) {
return this->GetLocalPoint(Vector4f(x, y, z, 1));
}
/** Translate using transformation chain */
using BaseClass::Translate;
/** Rotate using transformation chain */
using BaseClass::Rotate;
/** Scale using transformation chain */
using BaseClass::Scale;
signals:
// signal to emit when the box is updated //
virtual void Updated() override { ULIB_SIGNAL_EMIT(ContainerBox::Updated); }
private:
AffineTransform m_LocalT;
AffineTransform m_LocalT;
};
}
} // namespace uLib
#endif // CONTAINERBOX_H

141
src/Math/Cylinder.h Normal file
View File

@@ -0,0 +1,141 @@
/*//////////////////////////////////////////////////////////////////////////////
// CMT Cosmic Muon Tomography project //////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
Copyright (c) 2014, Universita' degli Studi di Padova, INFN sez. di Padova
All rights reserved
Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it >
------------------------------------------------------------------
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library.
//////////////////////////////////////////////////////////////////////////////*/
#ifndef U_CYLINDER_H
#define U_CYLINDER_H
#include "Geometry.h"
#include "Core/Object.h"
#include "Math/Dense.h"
#include "Math/Transform.h"
namespace uLib {
/**
* @brief Represents a cylindrical volume centered in the base circle.
*
* Cylinder inherits from AffineTransform, which defines its parent
* coordinate system. It contains an internal local transformation (m_LocalT)
* that defines the cylinder's actual volume (radius and height)
* relative to the emitter's origin (base circle center).
*/
class Cylinder : public AffineTransform, public Object {
typedef AffineTransform BaseClass;
public:
/**
* @brief Default constructor.
* Initializes with radius 1 and height 1.
*/
Cylinder() : m_LocalT(this), m_Radius(1.0), m_Height(1.0) {
UpdateLocalMatrix();
}
/**
* @brief Constructor with radius and height.
*/
Cylinder(float radius, float height) : m_LocalT(this), m_Radius(radius), m_Height(height) {
UpdateLocalMatrix();
}
/**
* @brief Copy constructor.
*/
Cylinder(const Cylinder &copy)
: m_LocalT(this), AffineTransform(copy) {
this->SetRadius(copy.GetRadius());
this->SetHeight(copy.GetHeight());
}
/** Sets the radius of the cylinder */
inline void SetRadius(float r) {
m_Radius = r;
UpdateLocalMatrix();
}
/** Gets the radius of the cylinder */
inline float GetRadius() const { return m_Radius; }
/** Sets the height of the cylinder */
inline void SetHeight(float h) {
m_Height = h;
UpdateLocalMatrix();
}
/** Gets the height of the cylinder */
inline float GetHeight() const { return m_Height; }
/**
* @brief Returns the world transformation matrix of the cylinder's volume.
*/
Matrix4f GetWorldMatrix() const { return m_LocalT.GetWorldMatrix(); }
/**
* @brief Returns the local transformation matrix of the cylinder's volume.
*/
Matrix4f GetLocalMatrix() const { return m_LocalT.GetMatrix(); }
/**
* @brief Transforms local cylindrical coordinates to world space.
* @param r Local radius (absolute).
* @param theta Local angle in radians.
* @param z Local height (absolute, relative to base circle).
* @return Transformed point in world space.
*/
inline Vector4f GetWorldPoint(float r, float theta, float z) const {
return BaseClass::GetWorldMatrix() * Vector4f(r * std::cos(theta), r * std::sin(theta), z, 1.0f);
}
/**
* @brief Transforms a world point to cylindrical local space.
* @return Vector3f(r, theta, z)
*/
inline Vector3f GetCylindricalLocal(const Vector4f &world_v) const {
Vector4f local_v = BaseClass::GetWorldMatrix().inverse() * world_v;
float r = std::sqrt(local_v.x() * local_v.x() + local_v.y() * local_v.y());
float theta = std::atan2(local_v.y(), local_v.x());
return Vector3f(r, theta, local_v.z());
}
signals:
/** Signal emitted when the cylinder geometry or transform is updated */
virtual void Updated() override { ULIB_SIGNAL_EMIT(Cylinder::Updated); }
private:
/** Recalculates the internal local matrix based on radius and height */
void UpdateLocalMatrix() {
m_LocalT = AffineTransform(this); // BaseClass is parent
m_LocalT.Scale(Vector3f(m_Radius, m_Radius, m_Height));
this->Updated();
}
float m_Radius;
float m_Height;
AffineTransform m_LocalT;
};
} // namespace uLib
#endif // U_CYLINDER_H

View File

@@ -36,7 +36,7 @@ namespace uLib {
class Geometry : public AffineTransform {
public:
inline Vector4f GetWorldPoint(const Vector4f &v) const {
inline Vector4f GetWorldPoint(const Vector4f v) const {
return this->GetWorldMatrix() * v;
}
@@ -44,7 +44,7 @@ public:
return this->GetWorldPoint(Vector4f(x,y,z,1));
}
inline Vector4f GetLocalPoint(const Vector4f &v) const {
inline Vector4f GetLocalPoint(const Vector4f v) const {
return this->GetWorldMatrix().inverse() * v;
}

86
src/Math/QuadMesh.cpp Normal file
View File

@@ -0,0 +1,86 @@
/*//////////////////////////////////////////////////////////////////////////////
// CMT Cosmic Muon Tomography project //////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
Copyright (c) 2014, Universita' degli Studi di Padova, INFN sez. di Padova
All rights reserved
Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it >
------------------------------------------------------------------
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library.
//////////////////////////////////////////////////////////////////////////////*/
#include "QuadMesh.h"
namespace uLib {
void QuadMesh::PrintSelf(std::ostream &o)
{
o << " // ------- QUAD MESH ------- // \n" ;
o << " #Points : " << m_Points.size() << "\n";
o << " #Quads : " << m_Quads.size() << "\n";
for(int i=0; i < m_Quads.size(); ++i ) {
o << " - quad[" << i << "]" <<
" " << m_Quads[i](0) << "->(" << GetPoint(m_Quads[i](0)).transpose() << ") " <<
" " << m_Quads[i](1) << "->(" << GetPoint(m_Quads[i](1)).transpose() << ") " <<
" " << m_Quads[i](2) << "->(" << GetPoint(m_Quads[i](2)).transpose() << ") " <<
" " << m_Quads[i](3) << "->(" << GetPoint(m_Quads[i](3)).transpose() << ") " <<
" \n";
}
o << " // ------------------------- // \n";
}
void QuadMesh::AddPoint(const Vector3f &pt)
{
Vector4f p(pt.x(), pt.y(), pt.z(), 1.0f);
Vector4f localP = this->GetWorldMatrix().inverse() * p;
this->m_Points.push_back(localP.head<3>());
}
Vector3f QuadMesh::GetPoint(const Id_t id) const
{
Vector4f p(m_Points.at(id).x(), m_Points.at(id).y(), m_Points.at(id).z(), 1.0f);
Vector4f worldP = this->GetWorldMatrix() * p;
return worldP.head<3>();
}
void QuadMesh::AddQuad(const Id_t *id)
{
Vector4i quad(id[0],id[1],id[2],id[3]);
this->m_Quads.push_back(quad);
}
void QuadMesh::AddQuad(const Vector4i &id)
{
this->m_Quads.push_back(id);
}
Vector3f QuadMesh::GetNormal(const Id_t id) const
{
const Vector4i &quad = m_Quads.at(id);
const Vector3f v0 = this->GetPoint(quad(0));
const Vector3f v1 = this->GetPoint(quad(1));
const Vector3f v3 = this->GetPoint(quad(3));
Vector3f edge1 = v1 - v0;
Vector3f edge2 = v3 - v0;
return edge1.cross(edge2).normalized();
}
}

View File

@@ -23,64 +23,46 @@
//////////////////////////////////////////////////////////////////////////////*/
#ifndef QUADMESH_H
#define QUADMESH_H
#include <vector>
#ifndef SOLID_H
#define SOLID_H
#include "Core/Object.h"
#include "Math/Dense.h"
#include "Math/TriangleMesh.h"
#include "Detectors/Matter.h"
class G4Material;
class G4LogicalVolume;
class G4TessellatedSolid;
#include "Core/Object.h"
#include "Math/Transform.h"
namespace uLib {
class Solid : public Object {
class QuadMesh : public AffineTransform, public Object
{
public:
void PrintSelf(std::ostream &o);
Solid();
Solid(const char *name);
/** @brief Adds a point in global coordinates. Stored in local coordinates. */
void AddPoint(const Vector3f &pt);
void SetNistMaterial(const char *name);
void AddQuad(const Id_t *id);
void AddQuad(const Vector4i &id);
void SetMaterial(G4Material *material);
/** @brief Returns point in global coordinates. */
Vector3f GetPoint(const Id_t id) const;
uLibGetMacro(Material,G4Material *)
uLibGetMacro(Logical,G4LogicalVolume *)
inline std::vector<Vector3f> & Points() { return this->m_Points; }
inline std::vector<Vector4i> & Quads() { return this->m_Quads; }
protected:
G4Material *m_Material;
G4LogicalVolume *m_Logical;
};
const Vector4i & GetQuad(const Id_t id) const { return m_Quads.at(id); }
Vector3f GetNormal(const Id_t id) const;
virtual void Updated() override { ULIB_SIGNAL_EMIT(QuadMesh::Updated); }
class TessellatedSolid : public Solid {
typedef Solid BaseClass;
public:
TessellatedSolid(const char *name);
void SetMesh(TriangleMesh &mesh);
uLibGetMacro(Solid,G4TessellatedSolid *)
private:
G4TessellatedSolid *m_Solid;
std::vector<Vector3f> m_Points;
std::vector<Vector4i> m_Quads;
};
}
#endif // SOLID_H
#endif // QUADMESH_H

View File

@@ -50,6 +50,7 @@
#define U_TRANSFORM_H
#include <Eigen/Geometry>
#include "Math/Dense.h"
namespace uLib {
@@ -84,8 +85,8 @@ public:
inline void SetParent(AffineTransform *name) { this->m_Parent = name; }
inline void SetMatrix (Matrix4f &mat) { m_T.matrix() = mat; }
inline Matrix4f& GetMatrix () { return m_T.matrix(); }
inline void SetMatrix (Matrix4f mat) { m_T.matrix() = mat; }
inline Matrix4f GetMatrix() const { return m_T.matrix(); }
Matrix4f GetWorldMatrix() const
{
@@ -93,22 +94,26 @@ public:
else return m_Parent->GetWorldMatrix() * m_T.matrix(); // T = B * A //
}
inline void SetPosition(const Vector3f &v) { this->m_T.translation() = v; }
inline void SetPosition(const Vector3f v) { this->m_T.translation() = v; }
inline Vector3f GetPosition() const { return this->m_T.translation(); }
inline void SetRotation(const Matrix3f &m) { this->m_T.linear() = m; }
inline void SetRotation(const Matrix3f m) { this->m_T.linear() = m; }
inline Matrix3f GetRotation() const { return this->m_T.rotation(); }
inline void Translate(const Vector3f &v) { this->m_T.translate(v); }
inline void Translate(const Vector3f v) { this->m_T.pretranslate(v); }
inline void Scale(const Vector3f &v) { this->m_T.scale(v); }
inline void Scale(const Vector3f v) { this->m_T.scale(v); }
inline Vector3f GetScale() const { return this->m_T.linear() * Vector3f(1,1,1); } // FIXXXXXXX
inline Vector3f GetScale() const {
return Vector3f(m_T.linear().col(0).norm(),
m_T.linear().col(1).norm(),
m_T.linear().col(2).norm());
}
inline void Rotate(const Matrix3f &m) { this->m_T.rotate(m); }
inline void Rotate(const Matrix3f m) { this->m_T.rotate(m); }
inline void Rotate(const float angle, Vector3f axis)
{
@@ -122,12 +127,12 @@ public:
Rotate(angle,euler_axis);
}
inline void PreRotate(const Matrix3f &m) { this->m_T.prerotate(m); }
inline void PreRotate(const Matrix3f m) { this->m_T.prerotate(m); }
inline void QuaternionRotate(const Vector4f &q)
inline void QuaternionRotate(const Vector4f q)
{ this->m_T.rotate(Eigen::Quaternion<float>(q)); }
inline void EulerYZYRotate(const Vector3f &e) {
inline void EulerYZYRotate(const Vector3f e) {
Matrix3f mat;
mat = Eigen::AngleAxisf(e.x(), Vector3f::UnitY())
* Eigen::AngleAxisf(e.y(), Vector3f::UnitZ())

View File

@@ -24,8 +24,6 @@
//////////////////////////////////////////////////////////////////////////////*/
#include "TriangleMesh.h"
@@ -36,14 +34,14 @@ void TriangleMesh::PrintSelf(std::ostream &o)
o << " // ------- TRIANGLE MESH ------- // \n" ;
o << " #Points : " << m_Points.size() << "\n";
o << " #Triang : " << m_Triangles.size() << "\n";
for(int i=0; i < m_Triangles.size(); ++i ) {
for(int i=0; i < (int)m_Triangles.size(); ++i ) {
o << " - triangle[" << i << "]" <<
" " << m_Triangles[i](0) <<
"->(" << m_Points[m_Triangles[i](0)].transpose() << ") " <<
"->(" << GetPoint(m_Triangles[i](0)).transpose() << ") " <<
" " << m_Triangles[i](1) <<
"->(" << m_Points[m_Triangles[i](1)].transpose() << ") " <<
"->(" << GetPoint(m_Triangles[i](1)).transpose() << ") " <<
" " << m_Triangles[i](2) <<
"->(" << m_Points[m_Triangles[i](2)].transpose() << ") " <<
"->(" << GetPoint(m_Triangles[i](2)).transpose() << ") " <<
" \n";
}
o << " // ----------------------------- // \n";
@@ -51,7 +49,16 @@ void TriangleMesh::PrintSelf(std::ostream &o)
void TriangleMesh::AddPoint(const Vector3f &pt)
{
this->m_Points.push_back(pt);
Vector4f p(pt.x(), pt.y(), pt.z(), 1.0f);
Vector4f localP = this->GetWorldMatrix().inverse() * p;
this->m_Points.push_back(localP.head<3>());
}
Vector3f TriangleMesh::GetPoint(const Id_t id) const
{
Vector4f p(m_Points.at(id).x(), m_Points.at(id).y(), m_Points.at(id).z(), 1.0f);
Vector4f worldP = this->GetWorldMatrix() * p;
return worldP.head<3>();
}
void TriangleMesh::AddTriangle(const Id_t *id)
@@ -65,5 +72,18 @@ void TriangleMesh::AddTriangle(const Vector3i &id)
this->m_Triangles.push_back(id);
}
Vector3f TriangleMesh::GetNormal(const Id_t id) const
{
const Vector3i &trg = m_Triangles.at(id);
const Vector3f v0 = this->GetPoint(trg(0));
const Vector3f v1 = this->GetPoint(trg(1));
const Vector3f v2 = this->GetPoint(trg(2));
Vector3f edge1 = v1 - v0;
Vector3f edge2 = v2 - v0;
return edge1.cross(edge2).normalized();
}
}

View File

@@ -32,21 +32,33 @@
#include "Math/Dense.h"
#include "Core/Object.h"
#include "Math/Transform.h"
namespace uLib {
class TriangleMesh
class TriangleMesh : public AffineTransform, public Object
{
public:
void PrintSelf(std::ostream &o);
/** @brief Adds a point in global coordinates. Stored in local coordinates. */
void AddPoint(const Vector3f &pt);
void AddTriangle(const Id_t *id);
void AddTriangle(const Vector3i &id);
/** @brief Returns point in global coordinates. */
Vector3f GetPoint(const Id_t id) const;
inline std::vector<Vector3f> & Points() { return this->m_Points; }
inline std::vector<Vector3i> & Triangles() { return this->m_Triangles; }
const Vector3i & GetTriangle(const Id_t id) const { return m_Triangles.at(id); }
Vector3f GetNormal(const Id_t id) const;
virtual void Updated() override { ULIB_SIGNAL_EMIT(TriangleMesh::Updated); }
private:
std::vector<Vector3f> m_Points;
std::vector<Vector3i> m_Triangles;

56
src/Math/Units.h Normal file
View File

@@ -0,0 +1,56 @@
#ifndef ULIB_MATH_UNITS_H
#define ULIB_MATH_UNITS_H
#ifdef HAVE_GEANT4
#include <CLHEP/Units/PhysicalConstants.h>
#include <CLHEP/Units/SystemOfUnits.h>
#else
#include "CLHEP_defs.hh"
#endif
namespace uLib {
using namespace CLHEP;
inline namespace literals {
constexpr double operator"" _m(long double v) { return static_cast<double>(v) * CLHEP::meter; }
constexpr double operator"" _cm(long double v) { return static_cast<double>(v) * CLHEP::centimeter; }
constexpr double operator"" _mm(long double v) { return static_cast<double>(v) * CLHEP::millimeter; }
constexpr double operator"" _um(long double v) { return static_cast<double>(v) * CLHEP::micrometer; }
constexpr double operator"" _nm(long double v) { return static_cast<double>(v) * CLHEP::nanometer; }
constexpr double operator"" _km(long double v) { return static_cast<double>(v) * CLHEP::kilometer; }
constexpr double operator"" _m(unsigned long long v) { return static_cast<double>(v) * CLHEP::meter; }
constexpr double operator"" _cm(unsigned long long v) { return static_cast<double>(v) * CLHEP::centimeter; }
constexpr double operator"" _mm(unsigned long long v) { return static_cast<double>(v) * CLHEP::millimeter; }
constexpr double operator"" _um(unsigned long long v) { return static_cast<double>(v) * CLHEP::micrometer; }
constexpr double operator"" _nm(unsigned long long v) { return static_cast<double>(v) * CLHEP::nanometer; }
constexpr double operator"" _km(unsigned long long v) { return static_cast<double>(v) * CLHEP::kilometer; }
constexpr double operator"" _deg(long double v) { return static_cast<double>(v) * CLHEP::degree; }
constexpr double operator"" _rad(long double v) { return static_cast<double>(v) * CLHEP::radian; }
constexpr double operator"" _deg(unsigned long long v) { return static_cast<double>(v) * CLHEP::degree; }
constexpr double operator"" _rad(unsigned long long v) { return static_cast<double>(v) * CLHEP::radian; }
constexpr double operator"" _ns(long double v) { return static_cast<double>(v) * CLHEP::nanosecond; }
constexpr double operator"" _s(long double v) { return static_cast<double>(v) * CLHEP::second; }
constexpr double operator"" _ms(long double v) { return static_cast<double>(v) * CLHEP::millisecond; }
constexpr double operator"" _ns(unsigned long long v) { return static_cast<double>(v) * CLHEP::nanosecond; }
constexpr double operator"" _s(unsigned long long v) { return static_cast<double>(v) * CLHEP::second; }
constexpr double operator"" _ms(unsigned long long v) { return static_cast<double>(v) * CLHEP::millisecond; }
constexpr double operator"" _MeV(long double v) { return static_cast<double>(v) * CLHEP::megaelectronvolt; }
constexpr double operator"" _eV(long double v) { return static_cast<double>(v) * CLHEP::electronvolt; }
constexpr double operator"" _keV(long double v) { return static_cast<double>(v) * CLHEP::kiloelectronvolt; }
constexpr double operator"" _GeV(long double v) { return static_cast<double>(v) * CLHEP::gigaelectronvolt; }
constexpr double operator"" _TeV(long double v) { return static_cast<double>(v) * CLHEP::teraelectronvolt; }
constexpr double operator"" _MeV(unsigned long long v) { return static_cast<double>(v) * CLHEP::megaelectronvolt; }
constexpr double operator"" _eV(unsigned long long v) { return static_cast<double>(v) * CLHEP::electronvolt; }
constexpr double operator"" _keV(unsigned long long v) { return static_cast<double>(v) * CLHEP::kiloelectronvolt; }
constexpr double operator"" _GeV(unsigned long long v) { return static_cast<double>(v) * CLHEP::gigaelectronvolt; }
}
}
#endif

View File

@@ -63,9 +63,16 @@ public:
inline size_t Count() const { return this->m_Count; }
inline size_t size() const { return this->m_Count; }
inline const Scalarf &TotalLength() const { return this->m_TotalLength; }
inline void SetCount(size_t c) { this->m_Count = c; }
inline void SetCount(size_t c) {
this->m_Count = c;
if (this->m_Data.size() != c) {
this->m_Data.resize(c);
}
}
inline void SetTotalLength(Scalarf tl) { this->m_TotalLength = tl; }

View File

@@ -3,6 +3,7 @@ set(TESTS
MathVectorTest
GeometryTest
ContainerBoxTest
CylinderTest
VoxImageTest
VoxRaytracerTest
VoxRaytracerTestExtended
@@ -12,7 +13,9 @@ set(TESTS
AccumulatorTest
VoxImageCopyTest
TriangleMeshTest
QuadMeshTest
BitCodeTest
UnitsTest
)
set(LIBRARIES

View File

@@ -31,6 +31,7 @@
#include "Math/Dense.h"
#include "Math/ContainerBox.h"
#include <cmath>
#include <iostream>
#include <math.h>
@@ -52,38 +53,112 @@ int main()
BEGIN_TESTING(Math ContainerBox);
ContainerBox Cnt;
// // Local transform:
Cnt.SetOrigin(Vector3f(-1,-1,-1));
Cnt.SetSize(Vector3f(2,2,2)); // scaling //
std::cout << "Container scale is: " << Cnt.GetSize().transpose() << "\n";
std::cout << "Container scale is: " << Cnt.GetSize().transpose() << "\n";
TEST0( Vector4f0(Cnt.GetSize().homogeneous() - HVector3f(2,2,2)) );
{
ContainerBox Cnt;
Cnt.SetOrigin(Vector3f(0,0,0));
Cnt.SetSize(Vector3f(2,2,2));
TEST0( Vector4f0(Cnt.GetOrigin().homogeneous() - HVector3f(0,0,0)) );
TEST0( Vector4f0(Cnt.GetSize().homogeneous() - HVector3f(2,2,2)) );
ContainerBox Box;
HPoint3f pt = Cnt.GetLocalPoint(HPoint3f(0,0,0));
HPoint3f wp = Cnt.GetWorldPoint(pt);
TEST0( Vector4f0(wp - HPoint3f(0,0,0)) );
Box.SetPosition(Vector3f(1,1,1));
Box.SetSize(Vector3f(2,2,2));
Box.EulerYZYRotate(Vector3f(0,0,0));
HPoint3f pt = Box.GetLocalPoint(HPoint3f(2,3,2));
HPoint3f wp = Box.GetWorldPoint(pt);
TEST0( Vector4f0(wp - HPoint3f(2,3,2)) );
HPoint3f pt2 = Cnt.GetLocalPoint(HPoint3f(2,2,2));
HPoint3f wp2 = Cnt.GetWorldPoint(pt2);
TEST0( Vector4f0(wp2 - HPoint3f(2,2,2)) );
HPoint3f pt3 = Cnt.GetLocalPoint(HPoint3f(1,1,1));
HPoint3f wp3 = Cnt.GetWorldPoint(pt3);
TEST0( Vector4f0(wp3 - HPoint3f(1,1,1)) );
HPoint3f pt4 = Cnt.GetLocalPoint(HPoint3f(1,2,3));
HPoint3f wp4 = Cnt.GetWorldPoint(pt4);
TEST0( Vector4f0(wp4 - HPoint3f(1,2,3)) );
}
{
ContainerBox Cnt;
Cnt.SetOrigin(Vector3f(0,0,0));
Cnt.SetSize(Vector3f(2,2,2));
Cnt.EulerYZYRotate(Vector3f(M_PI,0,0));
HPoint3f pt = Cnt.GetLocalPoint(HPoint3f(0,0,0));
HPoint3f wp = Cnt.GetWorldPoint(pt);
TEST0( Vector4f0(wp - HPoint3f(0,0,0)) );
HPoint3f pt2 = Cnt.GetLocalPoint(HPoint3f(2,2,2));
HPoint3f wp2 = Cnt.GetWorldPoint(pt2);
TEST0( Vector4f0(wp2 - HPoint3f(2,2,2)) );
pt2 = HPoint3f(1,1,1);
wp2 = Cnt.GetWorldPoint(pt2);
TEST0( Vector4f0(wp2 - HPoint3f(-2,2,-2)) );
pt2 = HPoint3f(1,2,3);
wp2 = Cnt.GetWorldPoint(pt2);
TEST0( Vector4f0(wp2 - HPoint3f(-2,4,-6)) );
}
{
ContainerBox Cnt;
Cnt.SetOrigin(Vector3f(-1,-1,-1));
Cnt.SetSize(Vector3f(2,2,2)); // scaling //
HPoint3f pt2 = HPoint3f(.5,.5,.5);
HPoint3f wp2 = Cnt.GetWorldPoint(pt2);
TEST0( Vector4f0(wp2 - HPoint3f(0,0,0)) );
pt2 = HPoint3f(0,0,0);
wp2 = Cnt.GetWorldPoint(pt2);
TEST0( Vector4f0(wp2 - HPoint3f(-1,-1,-1)) );
Cnt.EulerYZYRotate(Vector3f(M_PI,0,0));
pt2 = HPoint3f(0,0,0);
wp2 = Cnt.GetWorldPoint(pt2);
TEST0( Vector4f0(wp2 - HPoint3f(1,-1,1)) );
}
{
ContainerBox Box;
Box.SetOrigin(Vector3f(1,1,1));
Box.SetSize(Vector3f(2,2,2));
Box.EulerYZYRotate(Vector3f(0,0,0));
HPoint3f pt = Box.GetLocalPoint(HPoint3f(2,3,2));
HPoint3f wp = Box.GetWorldPoint(pt);
TEST0( Vector4f0(wp - HPoint3f(2,3,2)) );
}
{
ContainerBox Box;
Box.SetPosition(Vector3f(-0.5,-0.5,-0.5));
Box.SetSize(Vector3f(1,1,1));
HPoint3f pt = Box.GetLocalPoint(HPoint3f(0,0,0));
HPoint3f wp = Box.GetWorldPoint(pt);
TEST0( Vector4f0(wp - HPoint3f(0,0,0)) );
}
{
ContainerBox Box;
Box.SetOrigin(Vector3f(-1.5,-1.5,-1.5));
Box.SetSize(Vector3f(3,3,3));
HPoint3f pt = Box.GetLocalPoint(HPoint3f(0,0,0));
HPoint3f wp = Box.GetWorldPoint(pt);
TEST0( Vector4f0(wp - HPoint3f(0,0,0)) );
pt = HPoint3f(1,1,1);
wp = Box.GetWorldPoint(pt);
TEST0( Vector4f0(wp - HPoint3f(1.5, 1.5, 1.5)) );
Box.EulerYZYRotate(Vector3f(M_PI,0,0));
pt = HPoint3f(1,1,1);
wp = Box.GetWorldPoint(pt);
TEST0( Vector4f0(wp - HPoint3f(-1.5, 1.5, -1.5)) );
}
//// // Global
// Cnt.SetPosition(Vector3f(1,1,1));
// Cnt.EulerYZYRotate(Vector3f(M_PI_2,M_PI_2,0));
// HPoint3f p = Cnt.GetWorldPoint(1,1,1);
// //std::cout << p.transpose() << "\n";
// TEST0( Vector4f0(p - HVector3f(2,1,2)) );
// p = Cnt.GetWorldPoint(1,2,3);
// //std::cout << p.transpose() << "\n";
// TEST0( Vector4f0(p - HVector3f(4,1,3)) );
// // scaling //

View File

@@ -0,0 +1,120 @@
/*//////////////////////////////////////////////////////////////////////////////
// CMT Cosmic Muon Tomography project //////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
Copyright (c) 2014, Universita' degli Studi di Padova, INFN sez. di Padova
All rights reserved
Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it >
------------------------------------------------------------------
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library.
//////////////////////////////////////////////////////////////////////////////*/
#include "testing-prototype.h"
#include "Math/Dense.h"
#include "Math/Cylinder.h"
#include <cmath>
#include <iostream>
using namespace uLib;
/**
* @brief Utility function to check if a 4D vector (spatial part) is zero within a threshold.
* Returns 0 if zero, 1 otherwise.
*/
int Vector4f0(Vector4f c)
{
c(3) = 0;
if ( fabs(c(0)) < 0.001 && fabs(c(1)) < 0.001 && fabs(c(2)) < 0.001 )
return 0;
else
return 1;
}
int main()
{
BEGIN_TESTING(Math Cylinder);
// Test 1: Basic identity transformation and cylinder parameters
{
Cylinder cyl(2.0, 10.0);
std::cout << "Cyl R=" << cyl.GetRadius() << " H=" << cyl.GetHeight() << std::endl;
std::cout << "Cyl World Matrix:\n" << cyl.GetWorldMatrix() << std::endl;
TEST0( std::abs(cyl.GetRadius() - 2.0) > 0.001 );
TEST0( std::abs(cyl.GetHeight() - 10.0) > 0.001 );
// Point on the base circle center (Origin)
Vector4f p0 = cyl.GetWorldPoint(0, 0, 0);
std::cout << "p0: " << p0.transpose() << std::endl;
TEST0( Vector4f0(p0 - Vector4f(0, 0, 0, 1)) );
// Point on the top circle center (0, 0, Height)
Vector4f p1 = cyl.GetWorldPoint(0, 0, 10.0);
std::cout << "p1: " << p1.transpose() << std::endl;
TEST0( Vector4f0(p1 - Vector4f(0, 0, 10.0, 1)) );
// Point on the edge of the base circle at theta=0 (Radius, 0, 0)
Vector4f p2 = cyl.GetWorldPoint(2.0, 0, 0);
std::cout << "p2: " << p2.transpose() << std::endl;
TEST0( Vector4f0(p2 - Vector4f(2.0, 0, 0, 1)) );
// Point at 90 degrees on the side wall at middle height
Vector4f p3 = cyl.GetWorldPoint(2.0, M_PI/2.0, 5.0);
std::cout << "p3: " << p3.transpose() << std::endl;
TEST0( Vector4f0(p3 - Vector4f(0, 2.0, 5.0, 1)) );
}
// Test 2: Translation
{
Cylinder cyl(1.0, 2.0);
cyl.SetPosition(Vector3f(10, 20, 30));
// Local base origin (0, 0, 0) -> World (10, 20, 30)
Vector4f p0 = cyl.GetWorldPoint(0, 0, 0);
TEST0( Vector4f0(p0 - Vector4f(10, 20, 30, 1)) );
// Local top edge (1, 0, 2) -> World (11, 20, 32)
Vector4f p1 = cyl.GetWorldPoint(1, 0, 2);
TEST0( Vector4f0(p1 - Vector4f(11, 20, 32, 1)) );
}
// Test 3: Rotation and complex mapping
{
Cylinder cyl(5.0, 20.0);
cyl.SetPosition(Vector3f(1.0, 2.0, 3.0));
// Rotate 90 degrees around X: Local Y becomes World Z, Local Z becomes World -Y
cyl.Rotate(M_PI/2.0, Vector3f(1, 0, 0));
// Let's take a local point: r=5, theta=pi/2, z=10 -> (0, 5, 10) in local cartesian
// Transformed:
// Position: (1, 2, 3)
// Point relative to position: (0, -10, 5) [since Z local -> -Y world, Y local -> Z world]
// Final World: (1, 2-10, 3+5) = (1, -8, 8)
Vector4f world_p = cyl.GetWorldPoint(5.0, M_PI/2.0, 10.0);
TEST0( Vector4f0(world_p - Vector4f(1.0, -8.0, 8.0, 1)) );
// Test inverse mapping
Vector3f cyl_local = cyl.GetCylindricalLocal(world_p);
TEST0( std::abs(cyl_local.x() - 5.0) > 0.001 );
TEST0( std::abs(cyl_local.y() - M_PI/2.0) > 0.001 );
TEST0( std::abs(cyl_local.z() - 10.0) > 0.001 );
}
END_TESTING;
}

View File

@@ -14,7 +14,9 @@ TESTS = MathVectorTest \
PolicyTest \
AccumulatorTest \
VoxImageCopyTest \
TriangleMeshTest
TriangleMeshTest \
BitCodeTest \
UnitsTest
# else
# TEST =

View File

@@ -23,53 +23,41 @@
//////////////////////////////////////////////////////////////////////////////*/
#include "Math/TriangleMesh.h"
#include "Vtk/vtkTriangleMesh.h"
#include "testing-prototype.h"
#include "Math/QuadMesh.h"
#include <iostream>
using namespace uLib;
int main() {
BEGIN_TESTING(Quad Mesh);
int main()
{
BEGIN_TESTING(Vtk Triangle Mesh);
QuadMesh mesh;
// { // SIMPLE TESTS //
mesh.AddPoint(Vector3f(0, 0, 0));
mesh.AddPoint(Vector3f(1, 0, 0));
mesh.AddPoint(Vector3f(1, 1, 0));
mesh.AddPoint(Vector3f(0, 1, 0));
mesh.AddQuad(Vector4i(0, 1, 2, 3));
// TriangleMesh mesh;
mesh.PrintSelf(std::cout);
// mesh.AddPoint(Vector3f(0,0,0));
// mesh.AddPoint(Vector3f(0,1,0));
// mesh.AddPoint(Vector3f(1,0,0));
ASSERT_EQ(mesh.Points().size(), 4);
ASSERT_EQ(mesh.Quads().size(), 1);
// mesh.AddTriangle(Vector3i(0,1,2));
// Test transformation
mesh.Translate(Vector3f(10, 20, 30));
Vector3f p0 = mesh.GetPoint(0);
TEST1( (p0 - Vector3f(10, 20, 30)).norm() < 1e-6 );
// Test AddPoint during transformation
mesh.AddPoint(Vector3f(11, 21, 31)); // Should be stored as (1, 1, 1) locally
Id_t lastId = mesh.Points().size() - 1;
TEST1( (mesh.Points().at(lastId) - Vector3f(1, 1, 1)).norm() < 1e-5 );
// mesh.PrintSelf(std::cout);
mesh.PrintSelf(std::cout);
// vtkTriangleMesh v_mesh(mesh);
// v_mesh.Update();
// TestingRenderWidow(&v_mesh);
// }
{ // SIMPLE TESTS //
TriangleMesh mesh;
vtkTriangleMesh v_mesh(mesh);
v_mesh.ReadFromStlFile("prova.stl");
mesh.PrintSelf(std::cout);
TestingRenderWidow(&v_mesh);
}
END_TESTING;
END_TESTING;
}

View File

@@ -43,5 +43,20 @@ int main() {
mesh.PrintSelf(std::cout);
TEST1(mesh.Points().size() == 3);
TEST1(mesh.Triangles().size() == 1);
// Test transformation
mesh.Translate(Vector3f(10, 20, 30));
Vector3f p0 = mesh.GetPoint(0);
TEST1( (p0 - Vector3f(10, 20, 30)).norm() < 1e-6 );
// Test AddPoint during transformation
mesh.AddPoint(Vector3f(11, 21, 31)); // Should be stored as (1, 1, 1) locally
Id_t lastId = mesh.Points().size() - 1;
TEST1( (mesh.Points().at(lastId) - Vector3f(1, 1, 1)).norm() < 1e-5 );
mesh.PrintSelf(std::cout);
END_TESTING;
}

View File

@@ -0,0 +1,71 @@
/*//////////////////////////////////////////////////////////////////////////////
// CMT Cosmic Muon Tomography project //////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////
Copyright (c) 2014, Universita' degli Studi di Padova, INFN sez. di Padova
All rights reserved
Authors: Andrea Rigoni Garola < andrea.rigoni@pd.infn.it >
------------------------------------------------------------------
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 3.0 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library.
//////////////////////////////////////////////////////////////////////////////*/
#include "testing-prototype.h"
#include "Math/Units.h"
#include <cmath>
using namespace uLib;
int main()
{
BEGIN_TESTING(Math Units);
// Length
TEST0(1.0_m != 1000.0);
TEST0(1_m != 1000.0);
TEST0(1.0_cm != 10.0);
TEST0(1_cm != 10.0);
TEST0(1.0_mm != 1.0);
TEST0(1_mm != 1.0);
TEST0(1.0_km != 1000000.0);
TEST0(1_km != 1000000.0);
TEST0(std::abs(1.0_um - 1e-3) > 1e-12);
TEST0(std::abs(1.0_nm - 1e-6) > 1e-15);
// Angle
TEST0(std::abs(180.0_deg - M_PI) > 1e-9);
TEST0(std::abs(180_deg - M_PI) > 1e-9);
TEST0(std::abs(1.0_rad - 1.0) > 1e-9);
// Time
TEST0(1.0_s != 1e9);
TEST0(1_s != 1e9);
TEST0(1.0_ms != 1e6);
TEST0(1_ms != 1e6);
TEST0(1.0_ns != 1.0);
TEST0(1_ns != 1.0);
// Energy
TEST0(1.0_MeV != 1.0);
TEST0(1_MeV != 1.0);
TEST0(1.0_GeV != 1000.0);
TEST0(1_GeV != 1000.0);
TEST0(1.0_TeV != 1000000.0);
TEST0(1.0_keV != 1e-3);
TEST0(1.0_eV != 1e-6);
END_TESTING;
}

View File

@@ -48,7 +48,7 @@ int main() {
Raytracer rt(img);
const size_t NUM_RAYS = 1000000;
const size_t NUM_RAYS = 100000;
std::cout << "Generating " << NUM_RAYS
<< " random ray pairs across a 100x100x100 grid...\n";

View File

@@ -31,7 +31,8 @@
static int _fail = 0; \
printf("..:: Testing " #name " ::..\n");
#define TEST1(val) _fail += (val)==0
#define TEST0(val) _fail += (val)!=0
#define TEST1(val) if ((val)==0) { printf("Assertion failed: %s != 0\n", #val); _fail++; }
#define TEST0(val) if ((val)!=0) { printf("Assertion failed: %s != 0\n", #val); _fail++; }
#define END_TESTING return _fail;
#define ASSERT_EQ(a, b) if ((a) != (b)) { printf("Assertion failed: %s != %s\n", #a, #b); _fail++; }

View File

@@ -1,14 +0,0 @@
include $(top_srcdir)/Common.am
library_includedir = $(includedir)/libmutom-${PACKAGE_VERSION}/ParticlePhysics/Geant
library_include_HEADERS =
_PPGEANT_SOURCES =
noinst_LTLIBRARIES = libmutomppgeant.la
libmutomppgeant_la_SOURCES = ${_PPGEANT_SOURCES}

View File

@@ -1,11 +0,0 @@
include $(top_srcdir)/Common.am
library_includedir = $(includedir)/libmutom-${PACKAGE_VERSION}/ParticlePhysics/MuonTomography
library_include_HEADERS =
_PPMUTOM_SOURCES =
noinst_LTLIBRARIES = libmutomppmutom.la
libmutomppmutom_la_SOURCES = ${_PPMUTOM_SOURCES}

View File

@@ -25,7 +25,7 @@ target_include_directories(uLib_python PRIVATE
# Install uLib_python within the uLib install target
install(TARGETS uLib_python
EXPORT "${PROJECT_NAME}Targets"
EXPORT "uLibTargets"
RUNTIME DESTINATION ${INSTALL_BIN_DIR} COMPONENT bin
LIBRARY DESTINATION ${INSTALL_LIB_DIR} COMPONENT lib
ARCHIVE DESTINATION ${INSTALL_LIB_DIR} COMPONENT lib

View File

@@ -1,22 +1,23 @@
#include <pybind11/pybind11.h>
#include <pybind11/eigen.h>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/stl_bind.h>
#include <pybind11/numpy.h>
#include "Math/Dense.h"
#include "Math/Transform.h"
#include "Math/Geometry.h"
#include "Math/Accumulator.h"
#include "Math/ContainerBox.h"
#include "Math/StructuredData.h"
#include "Math/StructuredGrid.h"
#include "Math/Dense.h"
#include "Math/Geometry.h"
#include "Math/Structured2DGrid.h"
#include "Math/Structured4DGrid.h"
#include "Math/StructuredData.h"
#include "Math/StructuredGrid.h"
#include "Math/Transform.h"
#include "Math/TriangleMesh.h"
#include "Math/VoxRaytracer.h"
#include "Math/Accumulator.h"
#include "Math/QuadMesh.h"
#include "Math/VoxImage.h"
#include "Math/VoxRaytracer.h"
namespace py = pybind11;
using namespace uLib;
@@ -40,346 +41,435 @@ PYBIND11_MAKE_OPAQUE(uLib::Vector<VoxRaytracer::RayData::Element>);
template <typename MatrixType>
void bind_eigen_type(py::module_ &m, const char *name) {
using Scalar = typename MatrixType::Scalar;
constexpr bool is_vector = MatrixType::IsVectorAtCompileTime;
using Scalar = typename MatrixType::Scalar;
constexpr bool is_vector = MatrixType::IsVectorAtCompileTime;
// Default constructor (zeros)
m.def(name, []() -> MatrixType {
if constexpr (MatrixType::RowsAtCompileTime == Eigen::Dynamic || MatrixType::ColsAtCompileTime == Eigen::Dynamic) {
return MatrixType(); // Empty dynamic matrix
} else {
return MatrixType::Zero(); // Zero static matrix
}
});
// Specialized constructor for dynamic matrices
if constexpr (MatrixType::RowsAtCompileTime == Eigen::Dynamic || MatrixType::ColsAtCompileTime == Eigen::Dynamic) {
m.def(name, [](int rows, int cols) -> MatrixType {
MatrixType mat;
mat.setZero(rows, cols);
return mat;
});
// Default constructor (zeros)
m.def(name, []() -> MatrixType {
if constexpr (MatrixType::RowsAtCompileTime == Eigen::Dynamic ||
MatrixType::ColsAtCompileTime == Eigen::Dynamic) {
return MatrixType(); // Empty dynamic matrix
} else {
return MatrixType::Zero(); // Zero static matrix
}
});
// Initialize from list
m.def(name, [](py::list l) -> MatrixType {
MatrixType mat;
if constexpr (is_vector) {
mat.setZero(l.size());
for (size_t i = 0; i < l.size(); ++i) {
mat(i) = l[i].cast<Scalar>();
}
} else {
int rows = MatrixType::RowsAtCompileTime == Eigen::Dynamic ? (int)std::sqrt(l.size()) : MatrixType::RowsAtCompileTime;
int cols = MatrixType::ColsAtCompileTime == Eigen::Dynamic ? (int)std::sqrt(l.size()) : MatrixType::ColsAtCompileTime;
mat.setZero(rows, cols);
for (size_t i = 0; i < (size_t)l.size(); ++i) {
mat(i / cols, i % cols) = l[i].cast<Scalar>();
}
}
return mat;
// Specialized constructor for dynamic matrices
if constexpr (MatrixType::RowsAtCompileTime == Eigen::Dynamic ||
MatrixType::ColsAtCompileTime == Eigen::Dynamic) {
m.def(name, [](int rows, int cols) -> MatrixType {
MatrixType mat;
mat.setZero(rows, cols);
return mat;
});
}
// Initialize from py::array
m.def(name, [](py::array_t<Scalar, py::array::c_style | py::array::forcecast> arr) -> MatrixType {
auto buf = arr.request();
MatrixType mat;
if constexpr (is_vector) {
// Initialize from list
m.def(name, [](py::list l) -> MatrixType {
MatrixType mat;
if constexpr (is_vector) {
mat.setZero(l.size());
for (size_t i = 0; i < l.size(); ++i) {
mat(i) = l[i].cast<Scalar>();
}
} else {
int rows = MatrixType::RowsAtCompileTime == Eigen::Dynamic
? (int)std::sqrt(l.size())
: MatrixType::RowsAtCompileTime;
int cols = MatrixType::ColsAtCompileTime == Eigen::Dynamic
? (int)std::sqrt(l.size())
: MatrixType::ColsAtCompileTime;
mat.setZero(rows, cols);
for (size_t i = 0; i < (size_t)l.size(); ++i) {
mat(i / cols, i % cols) = l[i].cast<Scalar>();
}
}
return mat;
});
// Initialize from py::array
m.def(name,
[](py::array_t<Scalar, py::array::c_style | py::array::forcecast> arr)
-> MatrixType {
auto buf = arr.request();
MatrixType mat;
if constexpr (is_vector) {
mat.setZero(buf.size);
Scalar* ptr = static_cast<Scalar*>(buf.ptr);
for (ssize_t i = 0; i < buf.size; ++i) mat(i) = ptr[i];
} else {
Scalar *ptr = static_cast<Scalar *>(buf.ptr);
for (ssize_t i = 0; i < buf.size; ++i)
mat(i) = ptr[i];
} else {
int rows = buf.shape.size() > 0 ? (int)buf.shape[0] : 1;
int cols = buf.shape.size() > 1 ? (int)buf.shape[1] : 1;
mat.setZero(rows, cols);
Scalar* ptr = static_cast<Scalar*>(buf.ptr);
Scalar *ptr = static_cast<Scalar *>(buf.ptr);
for (int i = 0; i < rows; ++i) {
for (int j = 0; j < cols; ++j) {
mat(i, j) = ptr[i * cols + j];
}
for (int j = 0; j < cols; ++j) {
mat(i, j) = ptr[i * cols + j];
}
}
}
return mat;
});
}
return mat;
});
}
void init_math(py::module_ &m) {
// 1. Basic Eigen Types (Vectors and Matrices)
bind_eigen_type<Vector1f>(m, "Vector1f");
bind_eigen_type<Vector2f>(m, "Vector2f");
bind_eigen_type<Vector3f>(m, "Vector3f");
bind_eigen_type<Vector4f>(m, "Vector4f");
bind_eigen_type<Vector1i>(m, "Vector1i");
bind_eigen_type<Vector2i>(m, "Vector2i");
bind_eigen_type<Vector3i>(m, "Vector3i");
bind_eigen_type<Vector4i>(m, "Vector4i");
bind_eigen_type<Vector1d>(m, "Vector1d");
bind_eigen_type<Vector2d>(m, "Vector2d");
bind_eigen_type<Vector3d>(m, "Vector3d");
bind_eigen_type<Vector4d>(m, "Vector4d");
// 1. Basic Eigen Types (Vectors and Matrices)
bind_eigen_type<Vector1f>(m, "Vector1f");
bind_eigen_type<Vector2f>(m, "Vector2f");
bind_eigen_type<Vector3f>(m, "Vector3f");
bind_eigen_type<Vector4f>(m, "Vector4f");
bind_eigen_type<Vector1i>(m, "Vector1i");
bind_eigen_type<Vector2i>(m, "Vector2i");
bind_eigen_type<Vector3i>(m, "Vector3i");
bind_eigen_type<Vector4i>(m, "Vector4i");
bind_eigen_type<Vector1d>(m, "Vector1d");
bind_eigen_type<Vector2d>(m, "Vector2d");
bind_eigen_type<Vector3d>(m, "Vector3d");
bind_eigen_type<Vector4d>(m, "Vector4d");
bind_eigen_type<Matrix2f>(m, "Matrix2f");
bind_eigen_type<Matrix3f>(m, "Matrix3f");
bind_eigen_type<Matrix4f>(m, "Matrix4f");
bind_eigen_type<Matrix2i>(m, "Matrix2i");
bind_eigen_type<Matrix3i>(m, "Matrix3i");
bind_eigen_type<Matrix4i>(m, "Matrix4i");
bind_eigen_type<Matrix2d>(m, "Matrix2d");
bind_eigen_type<Matrix3d>(m, "Matrix3d");
bind_eigen_type<Matrix4d>(m, "Matrix4d");
bind_eigen_type<Matrix2f>(m, "Matrix2f");
bind_eigen_type<Matrix3f>(m, "Matrix3f");
bind_eigen_type<Matrix4f>(m, "Matrix4f");
bind_eigen_type<Matrix2i>(m, "Matrix2i");
bind_eigen_type<Matrix3i>(m, "Matrix3i");
bind_eigen_type<Matrix4i>(m, "Matrix4i");
bind_eigen_type<Matrix2d>(m, "Matrix2d");
bind_eigen_type<Matrix3d>(m, "Matrix3d");
bind_eigen_type<Matrix4d>(m, "Matrix4d");
bind_eigen_type<MatrixXi>(m, "MatrixXi");
bind_eigen_type<MatrixXf>(m, "MatrixXf");
bind_eigen_type<MatrixXd>(m, "MatrixXd");
bind_eigen_type<MatrixXi>(m, "MatrixXi");
bind_eigen_type<MatrixXf>(m, "MatrixXf");
bind_eigen_type<MatrixXd>(m, "MatrixXd");
// 2. Homogeneous types
py::class_<HPoint3f>(m, "HPoint3f")
.def(py::init<>())
.def(py::init<float, float, float>())
.def(py::init<Vector3f &>());
py::class_<HVector3f>(m, "HVector3f")
.def(py::init<>())
.def(py::init<float, float, float>())
.def(py::init<Vector3f &>());
py::class_<HLine3f>(m, "HLine3f")
.def(py::init<>())
.def_readwrite("origin", &HLine3f::origin)
.def_readwrite("direction", &HLine3f::direction);
py::class_<HError3f>(m, "HError3f")
.def(py::init<>())
.def_readwrite("position_error", &HError3f::position_error)
.def_readwrite("direction_error", &HError3f::direction_error);
// 2. Homogeneous types
py::class_<HPoint3f>(m, "HPoint3f")
.def(py::init<>())
.def(py::init<float, float, float>())
.def(py::init<Vector3f &>());
py::class_<HVector3f>(m, "HVector3f")
.def(py::init<>())
.def(py::init<float, float, float>())
.def(py::init<Vector3f &>());
py::class_<HLine3f>(m, "HLine3f")
.def(py::init<>())
.def_readwrite("origin", &HLine3f::origin)
.def_readwrite("direction", &HLine3f::direction);
py::class_<HError3f>(m, "HError3f")
.def(py::init<>())
.def_readwrite("position_error", &HError3f::position_error)
.def_readwrite("direction_error", &HError3f::direction_error);
// 3. Dynamic Vectors (uLib::Vector)
py::bind_vector<uLib::Vector<Scalari>>(m, "Vector_i")
.def("MoveToVRAM", &uLib::Vector<Scalari>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Scalari>::MoveToRAM);
py::bind_vector<uLib::Vector<Scalarui>>(m, "Vector_ui")
.def("MoveToVRAM", &uLib::Vector<Scalarui>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Scalarui>::MoveToRAM);
py::bind_vector<uLib::Vector<Scalarl>>(m, "Vector_l")
.def("MoveToVRAM", &uLib::Vector<Scalarl>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Scalarl>::MoveToRAM);
py::bind_vector<uLib::Vector<Scalarul>>(m, "Vector_ul")
.def("MoveToVRAM", &uLib::Vector<Scalarul>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Scalarul>::MoveToRAM);
py::bind_vector<uLib::Vector<Scalarf>>(m, "Vector_f")
.def("MoveToVRAM", &uLib::Vector<Scalarf>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Scalarf>::MoveToRAM);
py::bind_vector<uLib::Vector<Scalard>>(m, "Vector_d")
.def("MoveToVRAM", &uLib::Vector<Scalard>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Scalard>::MoveToRAM);
// 3. Dynamic Vectors (uLib::Vector)
py::bind_vector<uLib::Vector<Scalari>>(m, "Vector_i")
.def("MoveToVRAM", &uLib::Vector<Scalari>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Scalari>::MoveToRAM);
py::bind_vector<uLib::Vector<Scalarui>>(m, "Vector_ui")
.def("MoveToVRAM", &uLib::Vector<Scalarui>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Scalarui>::MoveToRAM);
py::bind_vector<uLib::Vector<Scalarl>>(m, "Vector_l")
.def("MoveToVRAM", &uLib::Vector<Scalarl>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Scalarl>::MoveToRAM);
py::bind_vector<uLib::Vector<Scalarul>>(m, "Vector_ul")
.def("MoveToVRAM", &uLib::Vector<Scalarul>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Scalarul>::MoveToRAM);
py::bind_vector<uLib::Vector<Scalarf>>(m, "Vector_f")
.def("MoveToVRAM", &uLib::Vector<Scalarf>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Scalarf>::MoveToRAM);
py::bind_vector<uLib::Vector<Scalard>>(m, "Vector_d")
.def("MoveToVRAM", &uLib::Vector<Scalard>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Scalard>::MoveToRAM);
py::bind_vector<uLib::Vector<Vector3f>>(m, "Vector_Vector3f")
.def("MoveToVRAM", &uLib::Vector<Vector3f>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Vector3f>::MoveToRAM);
py::bind_vector<uLib::Vector<Vector3i>>(m, "Vector_Vector3i")
.def("MoveToVRAM", &uLib::Vector<Vector3i>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Vector3i>::MoveToRAM);
py::bind_vector<uLib::Vector<Vector4f>>(m, "Vector_Vector4f")
.def("MoveToVRAM", &uLib::Vector<Vector4f>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Vector4f>::MoveToRAM);
py::bind_vector<uLib::Vector<Vector4i>>(m, "Vector_Vector4i")
.def("MoveToVRAM", &uLib::Vector<Vector4i>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Vector4i>::MoveToRAM);
py::bind_vector<uLib::Vector<Vector3d>>(m, "Vector_Vector3d")
.def("MoveToVRAM", &uLib::Vector<Vector3d>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Vector3d>::MoveToRAM);
py::bind_vector<uLib::Vector<Vector4d>>(m, "Vector_Vector4d")
.def("MoveToVRAM", &uLib::Vector<Vector4d>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Vector4d>::MoveToRAM);
py::bind_vector<uLib::Vector<Voxel>>(m, "Vector_Voxel")
.def("MoveToVRAM", &uLib::Vector<Voxel>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Voxel>::MoveToRAM);
py::bind_vector<uLib::Vector<VoxRaytracer::RayData::Element>>(m, "Vector_VoxRaytracerRayDataElement")
.def("MoveToVRAM", &uLib::Vector<VoxRaytracer::RayData::Element>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<VoxRaytracer::RayData::Element>::MoveToRAM);
py::bind_vector<uLib::Vector<Vector3f>>(m, "Vector_Vector3f")
.def("MoveToVRAM", &uLib::Vector<Vector3f>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Vector3f>::MoveToRAM);
py::bind_vector<uLib::Vector<Vector3i>>(m, "Vector_Vector3i")
.def("MoveToVRAM", &uLib::Vector<Vector3i>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Vector3i>::MoveToRAM);
py::bind_vector<uLib::Vector<Vector4f>>(m, "Vector_Vector4f")
.def("MoveToVRAM", &uLib::Vector<Vector4f>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Vector4f>::MoveToRAM);
py::bind_vector<uLib::Vector<Vector4i>>(m, "Vector_Vector4i")
.def("MoveToVRAM", &uLib::Vector<Vector4i>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Vector4i>::MoveToRAM);
py::bind_vector<uLib::Vector<Vector3d>>(m, "Vector_Vector3d")
.def("MoveToVRAM", &uLib::Vector<Vector3d>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Vector3d>::MoveToRAM);
py::bind_vector<uLib::Vector<Vector4d>>(m, "Vector_Vector4d")
.def("MoveToVRAM", &uLib::Vector<Vector4d>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Vector4d>::MoveToRAM);
py::bind_vector<uLib::Vector<Voxel>>(m, "Vector_Voxel")
.def("MoveToVRAM", &uLib::Vector<Voxel>::MoveToVRAM)
.def("MoveToRAM", &uLib::Vector<Voxel>::MoveToRAM);
py::bind_vector<uLib::Vector<VoxRaytracer::RayData::Element>>(
m, "Vector_VoxRaytracerRayDataElement")
.def("MoveToVRAM",
&uLib::Vector<VoxRaytracer::RayData::Element>::MoveToVRAM)
.def("MoveToRAM",
&uLib::Vector<VoxRaytracer::RayData::Element>::MoveToRAM);
// 4. Accumulators
py::class_<Accumulator_Mean<float>>(m, "Accumulator_Mean_f")
.def(py::init<>())
.def("AddPass", &Accumulator_Mean<float>::AddPass)
.def("__call__", py::overload_cast<const float>(&Accumulator_Mean<float>::operator()))
.def("__call__", py::overload_cast<>(&Accumulator_Mean<float>::operator(), py::const_));
// 4. Accumulators
py::class_<Accumulator_Mean<float>>(m, "Accumulator_Mean_f")
.def(py::init<>())
.def("AddPass", &Accumulator_Mean<float>::AddPass)
.def("__call__",
py::overload_cast<const float>(&Accumulator_Mean<float>::operator()))
.def("__call__", py::overload_cast<>(&Accumulator_Mean<float>::operator(),
py::const_));
py::class_<Accumulator_Mean<double>>(m, "Accumulator_Mean_d")
.def(py::init<>())
.def("AddPass", &Accumulator_Mean<double>::AddPass)
.def("__call__", py::overload_cast<const double>(&Accumulator_Mean<double>::operator()))
.def("__call__", py::overload_cast<>(&Accumulator_Mean<double>::operator(), py::const_));
py::class_<Accumulator_Mean<double>>(m, "Accumulator_Mean_d")
.def(py::init<>())
.def("AddPass", &Accumulator_Mean<double>::AddPass)
.def("__call__", py::overload_cast<const double>(
&Accumulator_Mean<double>::operator()))
.def("__call__", py::overload_cast<>(
&Accumulator_Mean<double>::operator(), py::const_));
py::class_<Accumulator_ABTrim<float>>(m, "Accumulator_ABTrim_f")
.def(py::init<>())
.def("SetABTrim", &Accumulator_ABTrim<float>::SetABTrim)
.def("__iadd__", [](Accumulator_ABTrim<float> &self, float val) { self += val; return &self; })
.def("__call__", &Accumulator_ABTrim<float>::operator());
py::class_<Accumulator_ABTrim<float>>(m, "Accumulator_ABTrim_f")
.def(py::init<>())
.def("SetABTrim", &Accumulator_ABTrim<float>::SetABTrim)
.def("__iadd__",
[](Accumulator_ABTrim<float> &self, float val) {
self += val;
return &self;
})
.def("__call__", &Accumulator_ABTrim<float>::operator());
py::class_<Accumulator_ABTrim<double>>(m, "Accumulator_ABTrim_d")
.def(py::init<>())
.def("SetABTrim", &Accumulator_ABTrim<double>::SetABTrim)
.def("__iadd__", [](Accumulator_ABTrim<double> &self, double val) { self += val; return &self; })
.def("__call__", &Accumulator_ABTrim<double>::operator());
py::class_<Accumulator_ABTrim<double>>(m, "Accumulator_ABTrim_d")
.def(py::init<>())
.def("SetABTrim", &Accumulator_ABTrim<double>::SetABTrim)
.def("__iadd__",
[](Accumulator_ABTrim<double> &self, double val) {
self += val;
return &self;
})
.def("__call__", &Accumulator_ABTrim<double>::operator());
py::class_<Accumulator_ABClip<float>>(m, "Accumulator_ABClip_f")
.def(py::init<>())
.def("SetABTrim", &Accumulator_ABClip<float>::SetABTrim)
.def("__iadd__", [](Accumulator_ABClip<float> &self, float val) { self += val; return &self; })
.def("__call__", &Accumulator_ABClip<float>::operator());
py::class_<Accumulator_ABClip<float>>(m, "Accumulator_ABClip_f")
.def(py::init<>())
.def("SetABTrim", &Accumulator_ABClip<float>::SetABTrim)
.def("__iadd__",
[](Accumulator_ABClip<float> &self, float val) {
self += val;
return &self;
})
.def("__call__", &Accumulator_ABClip<float>::operator());
py::class_<Accumulator_ABClip<double>>(m, "Accumulator_ABClip_d")
.def(py::init<>())
.def("SetABTrim", &Accumulator_ABClip<double>::SetABTrim)
.def("__iadd__", [](Accumulator_ABClip<double> &self, double val) { self += val; return &self; })
.def("__call__", &Accumulator_ABClip<double>::operator());
py::class_<Accumulator_ABClip<double>>(m, "Accumulator_ABClip_d")
.def(py::init<>())
.def("SetABTrim", &Accumulator_ABClip<double>::SetABTrim)
.def("__iadd__",
[](Accumulator_ABClip<double> &self, double val) {
self += val;
return &self;
})
.def("__call__", &Accumulator_ABClip<double>::operator());
// 5. Core Math Structures
py::class_<AffineTransform>(m, "AffineTransform")
.def(py::init<>())
.def("GetWorldMatrix", &AffineTransform::GetWorldMatrix)
.def("SetPosition", &AffineTransform::SetPosition)
.def("GetPosition", &AffineTransform::GetPosition)
.def("Translate", &AffineTransform::Translate)
.def("Scale", &AffineTransform::Scale)
.def("SetRotation", &AffineTransform::SetRotation)
.def("GetRotation", &AffineTransform::GetRotation)
.def("Rotate", py::overload_cast<const Matrix3f &>(&AffineTransform::Rotate))
.def("Rotate", py::overload_cast<const Vector3f>(&AffineTransform::Rotate))
.def("EulerYZYRotate", &AffineTransform::EulerYZYRotate)
.def("FlipAxes", &AffineTransform::FlipAxes);
// 5. Core Math Structures
py::class_<AffineTransform, std::shared_ptr<AffineTransform>>(m,
"AffineTransform")
.def(py::init<>())
.def("GetWorldMatrix", &AffineTransform::GetWorldMatrix)
.def("SetPosition", &AffineTransform::SetPosition)
.def("GetPosition", &AffineTransform::GetPosition)
.def("Translate", &AffineTransform::Translate)
.def("Scale", &AffineTransform::Scale)
.def("SetRotation", &AffineTransform::SetRotation)
.def("GetRotation", &AffineTransform::GetRotation)
.def("Rotate",
py::overload_cast<const Matrix3f>(&AffineTransform::Rotate))
.def("Rotate",
py::overload_cast<float, Vector3f>(&AffineTransform::Rotate))
.def("Rotate", py::overload_cast<Vector3f>(&AffineTransform::Rotate))
.def("EulerYZYRotate", &AffineTransform::EulerYZYRotate)
.def("FlipAxes", &AffineTransform::FlipAxes);
py::class_<Geometry, AffineTransform>(m, "Geometry")
.def(py::init<>())
.def("GetWorldPoint", py::overload_cast<const Vector4f &>(&Geometry::GetWorldPoint, py::const_))
.def("GetLocalPoint", py::overload_cast<const Vector4f &>(&Geometry::GetLocalPoint, py::const_));
py::class_<Geometry, AffineTransform, std::shared_ptr<Geometry>>(m, "Geometry")
.def(py::init<>())
.def("GetWorldPoint", py::overload_cast<const Vector4f>(
&Geometry::GetWorldPoint, py::const_))
.def("GetWorldPoint",
py::overload_cast<float, float, float>(&Geometry::GetWorldPoint))
.def("GetLocalPoint", py::overload_cast<const Vector4f>(
&Geometry::GetLocalPoint, py::const_))
.def("GetLocalPoint",
py::overload_cast<float, float, float>(&Geometry::GetLocalPoint));
py::class_<ContainerBox, AffineTransform>(m, "ContainerBox")
.def(py::init<>())
.def("SetOrigin", &ContainerBox::SetOrigin)
.def("GetOrigin", &ContainerBox::GetOrigin)
.def("SetSize", &ContainerBox::SetSize)
.def("GetSize", &ContainerBox::GetSize)
.def("GetWorldMatrix", &ContainerBox::GetWorldMatrix)
.def("GetWorldPoint", py::overload_cast<const Vector4f &>(&ContainerBox::GetWorldPoint, py::const_))
.def("GetLocalPoint", py::overload_cast<const Vector4f &>(&ContainerBox::GetLocalPoint, py::const_));
py::class_<ContainerBox, AffineTransform, Object, std::shared_ptr<ContainerBox>>(
m, "ContainerBox")
.def(py::init<>())
.def("SetOrigin", &ContainerBox::SetOrigin)
.def("GetOrigin", &ContainerBox::GetOrigin)
.def("SetSize", &ContainerBox::SetSize)
.def("GetSize", &ContainerBox::GetSize)
.def("GetLocalMatrix", &ContainerBox::GetLocalMatrix)
.def("GetWorldMatrix", &ContainerBox::GetWorldMatrix)
.def("GetWorldPoint", py::overload_cast<const Vector4f &>(
&ContainerBox::GetWorldPoint, py::const_))
.def("GetWorldPoint",
py::overload_cast<float, float, float>(&ContainerBox::GetWorldPoint))
.def("GetLocalPoint", py::overload_cast<const Vector4f &>(
&ContainerBox::GetLocalPoint, py::const_))
.def("GetLocalPoint",
py::overload_cast<float, float, float>(&ContainerBox::GetLocalPoint))
.def("FlipLocalAxes", &ContainerBox::FlipLocalAxes);
py::enum_<StructuredData::_Order>(m, "StructuredDataOrder")
.value("CustomOrder", StructuredData::CustomOrder)
.value("XYZ", StructuredData::XYZ)
.value("XZY", StructuredData::XZY)
.value("YXZ", StructuredData::YXZ)
.value("YZX", StructuredData::YZX)
.value("ZXY", StructuredData::ZXY)
.value("ZYX", StructuredData::ZYX)
.export_values();
py::enum_<StructuredData::_Order>(m, "StructuredDataOrder")
.value("CustomOrder", StructuredData::CustomOrder)
.value("XYZ", StructuredData::XYZ)
.value("XZY", StructuredData::XZY)
.value("YXZ", StructuredData::YXZ)
.value("YZX", StructuredData::YZX)
.value("ZXY", StructuredData::ZXY)
.value("ZYX", StructuredData::ZYX)
.export_values();
py::class_<StructuredData>(m, "StructuredData")
.def(py::init<const Vector3i &>())
.def("GetDims", &StructuredData::GetDims)
.def("SetDims", &StructuredData::SetDims)
.def("GetIncrements", &StructuredData::GetIncrements)
.def("SetIncrements", &StructuredData::SetIncrements)
.def("SetDataOrder", &StructuredData::SetDataOrder)
.def("GetDataOrder", &StructuredData::GetDataOrder)
.def("IsInsideGrid", &StructuredData::IsInsideGrid)
.def("Map", &StructuredData::Map)
.def("UnMap", &StructuredData::UnMap);
py::class_<StructuredData, std::shared_ptr<StructuredData>>(m, "StructuredData")
.def(py::init<const Vector3i &>())
.def("GetDims", &StructuredData::GetDims)
.def("SetDims", &StructuredData::SetDims)
.def("GetIncrements", &StructuredData::GetIncrements)
.def("SetIncrements", &StructuredData::SetIncrements)
.def("SetDataOrder", &StructuredData::SetDataOrder)
.def("GetDataOrder", &StructuredData::GetDataOrder)
.def("IsInsideGrid", &StructuredData::IsInsideGrid)
.def("Map", &StructuredData::Map)
.def("UnMap", &StructuredData::UnMap);
py::class_<StructuredGrid, ContainerBox, StructuredData>(m, "StructuredGrid")
.def(py::init<const Vector3i &>())
.def("SetSpacing", &StructuredGrid::SetSpacing)
.def("GetSpacing", &StructuredGrid::GetSpacing)
.def("IsInsideBounds", &StructuredGrid::IsInsideBounds)
.def("Find", [](StructuredGrid &self, Vector3f pt) {
return self.Find(HPoint3f(pt));
});
py::class_<StructuredGrid, ContainerBox, StructuredData,
std::shared_ptr<StructuredGrid>>(m, "StructuredGrid")
.def(py::init<const Vector3i &>())
.def("SetSpacing", &StructuredGrid::SetSpacing)
.def("GetSpacing", &StructuredGrid::GetSpacing)
.def("IsInsideBounds", &StructuredGrid::IsInsideBounds)
.def("Find", [](StructuredGrid &self, Vector3f pt) {
return self.Find(HPoint3f(pt));
});
py::class_<Structured2DGrid>(m, "Structured2DGrid")
.def(py::init<>())
.def("SetDims", &Structured2DGrid::SetDims)
.def("GetDims", &Structured2DGrid::GetDims)
.def("IsInsideGrid", &Structured2DGrid::IsInsideGrid)
.def("Map", &Structured2DGrid::Map)
.def("UnMap", &Structured2DGrid::UnMap)
.def("SetPhysicalSpace", &Structured2DGrid::SetPhysicalSpace)
.def("GetSpacing", &Structured2DGrid::GetSpacing)
.def("GetOrigin", &Structured2DGrid::GetOrigin)
.def("IsInsideBounds", &Structured2DGrid::IsInsideBounds)
.def("PhysicsToUnitSpace", &Structured2DGrid::PhysicsToUnitSpace)
.def("UnitToPhysicsSpace", &Structured2DGrid::UnitToPhysicsSpace)
.def("SetDebug", &Structured2DGrid::SetDebug);
py::class_<Structured2DGrid>(m, "Structured2DGrid")
.def(py::init<>())
.def("SetDims", &Structured2DGrid::SetDims)
.def("GetDims", &Structured2DGrid::GetDims)
.def("IsInsideGrid", &Structured2DGrid::IsInsideGrid)
.def("Map", &Structured2DGrid::Map)
.def("UnMap", &Structured2DGrid::UnMap)
.def("SetPhysicalSpace", &Structured2DGrid::SetPhysicalSpace)
.def("GetSpacing", &Structured2DGrid::GetSpacing)
.def("GetOrigin", &Structured2DGrid::GetOrigin)
.def("IsInsideBounds", &Structured2DGrid::IsInsideBounds)
.def("PhysicsToUnitSpace", &Structured2DGrid::PhysicsToUnitSpace)
.def("UnitToPhysicsSpace", &Structured2DGrid::UnitToPhysicsSpace)
.def("SetDebug", &Structured2DGrid::SetDebug);
py::class_<Structured4DGrid>(m, "Structured4DGrid")
.def(py::init<>())
.def("SetDims", &Structured4DGrid::SetDims)
.def("GetDims", &Structured4DGrid::GetDims)
.def("IsInsideGrid", &Structured4DGrid::IsInsideGrid)
.def("Map", &Structured4DGrid::Map)
.def("UnMap", &Structured4DGrid::UnMap)
.def("SetPhysicalSpace", &Structured4DGrid::SetPhysicalSpace)
.def("GetSpacing", &Structured4DGrid::GetSpacing)
.def("GetOrigin", &Structured4DGrid::GetOrigin)
.def("IsInsideBounds", &Structured4DGrid::IsInsideBounds)
.def("PhysicsToUnitSpace", &Structured4DGrid::PhysicsToUnitSpace)
.def("UnitToPhysicsSpace", &Structured4DGrid::UnitToPhysicsSpace)
.def("SetDebug", &Structured4DGrid::SetDebug);
py::class_<Structured4DGrid>(m, "Structured4DGrid")
.def(py::init<>())
.def("SetDims", &Structured4DGrid::SetDims)
.def("GetDims", &Structured4DGrid::GetDims)
.def("IsInsideGrid", &Structured4DGrid::IsInsideGrid)
.def("Map", &Structured4DGrid::Map)
.def("UnMap", &Structured4DGrid::UnMap)
.def("SetPhysicalSpace", &Structured4DGrid::SetPhysicalSpace)
.def("GetSpacing", &Structured4DGrid::GetSpacing)
.def("GetOrigin", &Structured4DGrid::GetOrigin)
.def("IsInsideBounds", &Structured4DGrid::IsInsideBounds)
.def("PhysicsToUnitSpace", &Structured4DGrid::PhysicsToUnitSpace)
.def("UnitToPhysicsSpace", &Structured4DGrid::UnitToPhysicsSpace)
.def("SetDebug", &Structured4DGrid::SetDebug);
// 6. High-level Structures
py::class_<Voxel>(m, "Voxel")
.def(py::init<>())
.def_readwrite("Value", &Voxel::Value)
.def_readwrite("Count", &Voxel::Count);
// 6. High-level Structures
py::class_<Voxel>(m, "Voxel")
.def(py::init<>())
.def_readwrite("Value", &Voxel::Value)
.def_readwrite("Count", &Voxel::Count);
py::class_<Abstract::VoxImage, StructuredGrid>(m, "AbstractVoxImage")
.def("GetValue", py::overload_cast<const Vector3i &>(&Abstract::VoxImage::GetValue, py::const_))
.def("GetValue", py::overload_cast<const int>(&Abstract::VoxImage::GetValue, py::const_))
.def("SetValue", py::overload_cast<const Vector3i &, float>(&Abstract::VoxImage::SetValue))
.def("SetValue", py::overload_cast<const int, float>(&Abstract::VoxImage::SetValue))
.def("ExportToVtk", &Abstract::VoxImage::ExportToVtk)
.def("ExportToVti", &Abstract::VoxImage::ExportToVti)
.def("ImportFromVtk", &Abstract::VoxImage::ImportFromVtk)
.def("ImportFromVti", &Abstract::VoxImage::ImportFromVti);
py::class_<Abstract::VoxImage, StructuredGrid,
std::shared_ptr<Abstract::VoxImage>>(m, "AbstractVoxImage")
.def("GetValue", py::overload_cast<const Vector3i &>(
&Abstract::VoxImage::GetValue, py::const_))
.def("GetValue", py::overload_cast<const int>(
&Abstract::VoxImage::GetValue, py::const_))
.def("SetValue", py::overload_cast<const Vector3i &, float>(
&Abstract::VoxImage::SetValue))
.def("SetValue",
py::overload_cast<const int, float>(&Abstract::VoxImage::SetValue))
.def("ExportToVtk", &Abstract::VoxImage::ExportToVtk)
.def("ExportToVti", &Abstract::VoxImage::ExportToVti)
.def("ImportFromVtk", &Abstract::VoxImage::ImportFromVtk)
.def("ImportFromVti", &Abstract::VoxImage::ImportFromVti);
py::class_<VoxImage<Voxel>, Abstract::VoxImage>(m, "VoxImage")
.def(py::init<>())
.def(py::init<const Vector3i &>())
.def("Data", &VoxImage<Voxel>::Data, py::return_value_policy::reference_internal)
.def("InitVoxels", &VoxImage<Voxel>::InitVoxels)
.def("Abs", &VoxImage<Voxel>::Abs)
.def("clipImage", py::overload_cast<const Vector3i, const Vector3i>(&VoxImage<Voxel>::clipImage, py::const_))
.def("clipImage", py::overload_cast<const HPoint3f, const HPoint3f>(&VoxImage<Voxel>::clipImage, py::const_))
.def("clipImage", py::overload_cast<const float>(&VoxImage<Voxel>::clipImage, py::const_))
.def("maskImage", py::overload_cast<const HPoint3f, const HPoint3f, float>(&VoxImage<Voxel>::maskImage, py::const_))
.def("maskImage", py::overload_cast<const float, float, float>(&VoxImage<Voxel>::maskImage, py::const_), py::arg("threshold"), py::arg("belowValue") = 0, py::arg("aboveValue") = 0)
.def("fixVoxels", py::overload_cast<const float, float>(&VoxImage<Voxel>::fixVoxels, py::const_))
.def("__getitem__", py::overload_cast<unsigned int>(&VoxImage<Voxel>::operator[]))
.def("__getitem__", py::overload_cast<const Vector3i &>(&VoxImage<Voxel>::operator[]));
py::class_<VoxImage<Voxel>, Abstract::VoxImage,
std::shared_ptr<VoxImage<Voxel>>>(m, "VoxImage")
.def(py::init<>())
.def(py::init<const Vector3i &>())
.def("Data", &VoxImage<Voxel>::Data,
py::return_value_policy::reference_internal)
.def("InitVoxels", &VoxImage<Voxel>::InitVoxels)
.def("Abs", &VoxImage<Voxel>::Abs)
.def("clipImage", py::overload_cast<const Vector3i, const Vector3i>(
&VoxImage<Voxel>::clipImage, py::const_))
.def("clipImage", py::overload_cast<const HPoint3f, const HPoint3f>(
&VoxImage<Voxel>::clipImage, py::const_))
.def("clipImage", py::overload_cast<const float>(
&VoxImage<Voxel>::clipImage, py::const_))
.def("maskImage",
py::overload_cast<const HPoint3f, const HPoint3f, float>(
&VoxImage<Voxel>::maskImage, py::const_))
.def("maskImage",
py::overload_cast<const float, float, float>(
&VoxImage<Voxel>::maskImage, py::const_),
py::arg("threshold"), py::arg("belowValue") = 0,
py::arg("aboveValue") = 0)
.def("fixVoxels", py::overload_cast<const float, float>(
&VoxImage<Voxel>::fixVoxels, py::const_))
.def("__getitem__",
py::overload_cast<unsigned int>(&VoxImage<Voxel>::operator[]))
.def("__getitem__",
py::overload_cast<const Vector3i &>(&VoxImage<Voxel>::operator[]));
py::class_<TriangleMesh>(m, "TriangleMesh")
.def(py::init<>())
.def("AddPoint", &TriangleMesh::AddPoint)
.def("AddTriangle", py::overload_cast<const Vector3i &>(&TriangleMesh::AddTriangle))
.def("Points", &TriangleMesh::Points, py::return_value_policy::reference_internal)
.def("Triangles", &TriangleMesh::Triangles, py::return_value_policy::reference_internal);
py::class_<TriangleMesh>(m, "TriangleMesh")
.def(py::init<>())
.def("AddPoint", &TriangleMesh::AddPoint)
.def("AddTriangle",
py::overload_cast<const Vector3i &>(&TriangleMesh::AddTriangle))
.def("Points", &TriangleMesh::Points,
py::return_value_policy::reference_internal)
.def("Triangles", &TriangleMesh::Triangles,
py::return_value_policy::reference_internal)
.def("GetTriangle", &TriangleMesh::GetTriangle)
.def("GetNormal", &TriangleMesh::GetNormal);
py::class_<VoxRaytracer::RayData::Element>(m, "VoxRaytracerRayDataElement")
.def(py::init<>())
.def_readwrite("vox_id", &VoxRaytracer::RayData::Element::vox_id)
.def_readwrite("L", &VoxRaytracer::RayData::Element::L);
py::class_<QuadMesh>(m, "QuadMesh")
.def(py::init<>())
.def("AddPoint", &QuadMesh::AddPoint)
.def("AddQuad",
py::overload_cast<const Vector4i &>(&QuadMesh::AddQuad))
.def("Points", &QuadMesh::Points,
py::return_value_policy::reference_internal)
.def("Quads", &QuadMesh::Quads,
py::return_value_policy::reference_internal)
.def("GetQuad", &QuadMesh::GetQuad)
.def("GetNormal", &QuadMesh::GetNormal);
py::class_<VoxRaytracer::RayData>(m, "VoxRaytracerRayData")
.def(py::init<>())
.def("AppendRay", &VoxRaytracer::RayData::AppendRay)
.def("Data", py::overload_cast<>(&VoxRaytracer::RayData::Data), py::return_value_policy::reference_internal)
.def("Count", &VoxRaytracer::RayData::Count)
.def("TotalLength", &VoxRaytracer::RayData::TotalLength)
.def("SetCount", &VoxRaytracer::RayData::SetCount)
.def("SetTotalLength", &VoxRaytracer::RayData::SetTotalLength);
py::class_<VoxRaytracer::RayData::Element>(m, "VoxRaytracerRayDataElement")
.def(py::init<>())
.def_readwrite("vox_id", &VoxRaytracer::RayData::Element::vox_id)
.def_readwrite("L", &VoxRaytracer::RayData::Element::L);
py::class_<VoxRaytracer>(m, "VoxRaytracer")
.def(py::init<StructuredGrid &>(), py::keep_alive<1, 2>())
.def("GetImage", &VoxRaytracer::GetImage, py::return_value_policy::reference_internal)
.def("TraceLine", &VoxRaytracer::TraceLine)
.def("TraceBetweenPoints", &VoxRaytracer::TraceBetweenPoints);
py::class_<VoxRaytracer::RayData>(m, "VoxRaytracerRayData")
.def(py::init<>())
.def("AppendRay", &VoxRaytracer::RayData::AppendRay)
.def("Data", py::overload_cast<>(&VoxRaytracer::RayData::Data),
py::return_value_policy::reference_internal)
.def("Count", &VoxRaytracer::RayData::Count)
.def("TotalLength", &VoxRaytracer::RayData::TotalLength)
.def("SetCount", &VoxRaytracer::RayData::SetCount)
.def("SetTotalLength", &VoxRaytracer::RayData::SetTotalLength);
py::class_<VoxRaytracer>(m, "VoxRaytracer")
.def(py::init<StructuredGrid &>(), py::keep_alive<1, 2>())
.def("GetImage", &VoxRaytracer::GetImage,
py::return_value_policy::reference_internal)
.def("TraceLine", &VoxRaytracer::TraceLine)
.def("TraceBetweenPoints", &VoxRaytracer::TraceBetweenPoints);
}

View File

@@ -155,6 +155,18 @@ class TestMathNewTypes(unittest.TestCase):
# SetValue(id, value) sets At(id).Value = value
self.assertAlmostEqual(img.GetValue([0, 0, 0]), 10.5)
def test_quad_mesh(self):
mesh = uLib.Math.QuadMesh()
mesh.AddPoint(uLib.Math.Vector3f([0, 0, 0]))
mesh.AddPoint(uLib.Math.Vector3f([1, 0, 0]))
mesh.AddPoint(uLib.Math.Vector3f([1, 1, 0]))
mesh.AddPoint(uLib.Math.Vector3f([0, 1, 0]))
mesh.AddQuad(uLib.Math.Vector4i([0, 1, 2, 3]))
self.assertEqual(len(mesh.Points()), 4)
self.assertEqual(len(mesh.Quads()), 1)
class TestMathVoxRaytracer(unittest.TestCase):
def test_raytracer(self):
grid = uLib.Math.StructuredGrid([10, 10, 10])

View File

@@ -1,7 +1,10 @@
try:
from .uLib_python import Core, Math
except ImportError:
# Handle cases where the binary extension is not yet built
pass
try:
from uLib_python import Core, Math
except ImportError:
# Handle cases where the binary extension is not yet built
pass
__all__ = ["Core", "Math"]

Some files were not shown because too many files have changed in this diff Show More