jacg / nain4

An API that makes it easier to write Geant4 application code.
https://jacg.github.io/nain4/
2 stars 3 forks source link

built with nix

GHA tests

What is this?

nain4 is an API and accompanying set of libraries whose aim is to make it easier to write, test and deploy Geant4 applications.

Documentation

The documentation for nain4 is gradually being written here.

Nix

Nain4 uses Nix to manage dependencies, installation and provision of the development environment. The value proposition is: If you install nix on your machine we can provide a zero-effort means of installing Geant4 plus dependencies and development tools, and making sure that everything has compatible versions and works together in harmony[^1].

[^1]: Somewhere in the repository, we have provided the means to install nain4 without the use of Nix, but we do not have the resources or the motivation to maintain this. If something in nain4 is broken when using it via Nix, then we will aim to fix it; if something is broken when using nain4 without Nix, then we won't be able to help.

For HPC systems on which installing Nix might be problematic:

nain4

Nain4 is a collection of utilities whose aim is to

  1. make it significantly easier to write Geant4 applications, in code that is much more robust, readable and self-documenting;
  2. make it possible/easy to write unit and integration tests for Geant4 application code;
  3. make it more difficult to write invalid Geant4 code by promoting errors to compile time;

As a quick taster, here is a translation of the detector geometry from Geant4 example basic/B1 into nain4:

// Materials
auto water  = n4::material("G4_WATER");
auto air    = n4::material("G4_AIR");
auto tissue = n4::material("G4_A-150_TISSUE");
auto bone   = n4::material("G4_BONE_COMPACT_ICRU");

// Dimensions
// ...
// world_sizeXY = ... unremarkable value settings elided for brevity
// ...

// Volumes
auto world     = n4::box ("World"   ).xy(world_sizeXY).z(world_sizeZ)  .volume(air);
auto envelope  = n4::box ("Envelope").xy(  env_sizeXY).z(  env_sizeZ)  .volume(water);
auto trapezoid = n4::trd ("Bone"    ).x1(trd_dxa).x2(trd_dxb)
                                     .y1(trd_dya).y2(trd_dyb).z(trd_dz).volume(bone);

// If no handle is needed for a logical volume, it can be placed immediately
n4::cons("Tissue").r1(cone_rmaxa).r2(cone_rmaxb).z(cone_hz).place(tissue).in(envelope).at_yz(2*cm, -7*cm).now();

// Set Trapezoid as scoring volume
this -> fScoringVolume = trapezoid;

// Placement
n4::       place(envelope) .in(world)                      .now();
n4::       place(trapezoid).in(envelope).at_yz(-1*cm, 7*cm).now();
return n4::place(world)                                    .now();

This is the complete (except for setting of the values like world_sizeXYZ) nain4 implementation of DetectorConstruction::Construct().

These 13 lines of code (without comments or blank lines) correspond to 62 lines in the original example.

In Geant4's interfaces, you have to remember (or look up) the order of the parameters of the shape constructors, and you must provide values for each parameter, even when you want to use an obvious default value (such as inner radius being zero, or phi covering ); in nain4 the parameters have clear names and can be provided in any order that you find convenient; obvious default values can be omitted.

Geant4 obliges you to express everything in (frequently annoying) half-lengths; nain4 gives you the choice: .x vs .half_x.

The whole[^2] B1 example is translated into nain4 here, and, even though it is written in a style that places almost every argument on a separate line, it fits in a single file in under 150 lines, compared to the 1138 lines spread over 13 files in the original Geant4 rendition.

[^2]: This is not strictly true. The original B1 example caters for multiprocessing. As we actively discourage (TODO link to section in docs) using multiprocessing in Geant4, this translation into nain4 is not exactly equivalent. However, the differences are absolutely minimal.

Getting started

  1. install nix

    curl --proto '=https' --tlsv1.2 -sSf -L https://install.determinate.systems/nix | sh -s -- install
  2. Bootstrap a new nain4 project:

    nix run github:jacg/nain4#bootstrap-client-project path/to/your-new-project your-project-name "Your project description"

    This step will take a while as it downloads and compiles Geant4.

    A new directory path/to/your-new-project will be created. Adapt this path to your needs before executing the command.

    Also adapt your-project-name (used in various identifiers and path components inside your project) and "Your project description" before executing the command.

  3. cd into path/to/your-new-project

  4. Type nix develop [^3]

    Start a new bash shell in which all the required dependencies are available. Exiting this shell makes these dependencies invisible once more. Thus, the installation of these dependencies does not interfere with anything else you may have on your system.

  5. Type just run -g -n 10.

    This step compiles the example application in your new project and runs it in interactive mode. A visualization window should pop up.

[^3]: see the section on direnv for a more ergonomic alternative

If all this worked, hooray! You have a git repository containing a development environment in which you can use to evolve, test and deploy your nain4-based Geant4 application.

If not, read on to see possible problems and fixes.

Automatic environment switching with direnv

As it stands you have to write nix develop in order to activate the environment necessary to run and develop this code. What is more, nix develop places you in a minimal bash shell in which any personal configurations you may be used to, will be missing.

Both of these problems can be fixed with direnv which:

To use direnv:

  1. Make sure that it is installed on your system. If you have got this far, then you have already installed the Nix package manager on your machine, so you could use it to install direnv like this:

    nix profile install nixpkgs#direnv
  2. Don't forget to hook direnv into your shell. Depending on which shell you are using, this will involve adding one of the following lines to the end of your shell configuration file:

    eval "$(direnv hook bash)"  # in ~/.bashrc
    eval "$(direnv hook zsh)"   # in ~/.zshrc
    eval `direnv hook tcsh`     # in ~/.cshrc

The first time direnv wants to perform an automatic switch in a new context (combination of directory + .envrc contents), it asks you for permission to do so. You can give it permission by typing direnv allow in the shell. The message that direnv gives you at this stage is pretty clear, but it's usually written in red, thus you might get the mistaken impression that there is an error.

Operating systems

Graphics drivers

The interactive mode uses a Qt-based GUI. This requires the correct graphics drivers to be installed in a location known to Nix. This should work out of the box on

On other systems, that is to say non-NixOS Linuxes and WSL2 on Windows, the environment will try to use nixGL to provide the appropriate drivers. WARNING: the first time nixGL is needed, it will take a long time to download and compile.