Skip to content

Commit 3010bbf

Browse files
committed
added deploy to docker hub personal space
1 parent fc23a36 commit 3010bbf

7 files changed

Lines changed: 241 additions & 5 deletions

File tree

.github/workflows/deploy.yml

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,11 @@ jobs:
205205
matrix: ${{ fromJSON(needs.discover.outputs.matrix_manifest) }}
206206
env:
207207
TAG4: ${{ format('{0}-{1}-{2}', matrix.gemc_tag, matrix.image, matrix.image_tag) }}
208+
# Docker Hub mirror target. Steps are skipped automatically when the
209+
# DOCKERHUB_USERNAME secret is not configured (e.g. on forks).
210+
# Override the namespace with the DOCKERHUB_REPO repo variable.
211+
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
212+
DOCKERHUB_REPO: ${{ vars.DOCKERHUB_REPO || 'docker.io/maureeungaro/gemc' }}
208213
steps:
209214
- name: Checkout repository
210215
uses: actions/checkout@v6
@@ -218,6 +223,14 @@ jobs:
218223
username: ${{ github.actor }}
219224
password: ${{ secrets.GITHUB_TOKEN }}
220225

226+
- name: Log in to Docker Hub
227+
if: ${{ env.DOCKERHUB_USERNAME != '' }}
228+
uses: docker/login-action@v4
229+
with:
230+
registry: docker.io
231+
username: ${{ secrets.DOCKERHUB_USERNAME }}
232+
password: ${{ secrets.DOCKERHUB_TOKEN }}
233+
221234
- name: Set up Buildx
222235
uses: docker/setup-buildx-action@v4
223236

@@ -243,6 +256,17 @@ jobs:
243256
docker buildx imagetools create -t "$BASE_TAG" "$AMD_TAG"
244257
fi
245258
259+
- name: Mirror manifest to Docker Hub
260+
if: ${{ env.DOCKERHUB_USERNAME != '' }}
261+
shell: bash
262+
run: |
263+
# Copy the already-built multi-arch manifest from GHCR to Docker Hub
264+
# registry-to-registry (no pull, no rebuild).
265+
SRC="${{ needs.discover.outputs.image }}:${{ env.TAG4 }}"
266+
DST="${DOCKERHUB_REPO}:${{ env.TAG4 }}"
267+
echo "Mirroring $SRC -> $DST"
268+
docker buildx imagetools create -t "$DST" "$SRC"
269+
246270
- name: Download logs artifacts (all arches)
247271
if: ${{ always() }}
248272
uses: actions/download-artifact@v7
@@ -274,6 +298,37 @@ jobs:
274298
path: ${{ env.MANIFEST_SUMMARY_FILE }}
275299
if-no-files-found: warn
276300

301+
# Push the Docker Hub overview README once per deploy. Skipped automatically
302+
# when the DOCKERHUB_USERNAME secret is absent (e.g. on forks).
303+
dockerhub_readme:
304+
name: Update Docker Hub description
305+
needs: manifest
306+
if: ${{ always() && needs.manifest.result == 'success' }}
307+
runs-on: ubuntu-latest
308+
env:
309+
DOCKERHUB_USERNAME: ${{ secrets.DOCKERHUB_USERNAME }}
310+
DOCKERHUB_REPO: ${{ vars.DOCKERHUB_REPO || 'docker.io/maureeungaro/gemc' }}
311+
steps:
312+
- name: Checkout repository
313+
uses: actions/checkout@v6
314+
with:
315+
ref: ${{ github.event.workflow_run.head_sha }}
316+
317+
- name: Resolve Docker Hub repository path
318+
if: ${{ env.DOCKERHUB_USERNAME != '' }}
319+
shell: bash
320+
run: echo "DH_REPO_PATH=${DOCKERHUB_REPO#docker.io/}" >> "$GITHUB_ENV"
321+
322+
- name: Push README to Docker Hub
323+
if: ${{ env.DOCKERHUB_USERNAME != '' }}
324+
uses: peter-evans/dockerhub-description@v4
325+
with:
326+
username: ${{ secrets.DOCKERHUB_USERNAME }}
327+
password: ${{ secrets.DOCKERHUB_TOKEN }}
328+
repository: ${{ env.DH_REPO_PATH }}
329+
short-description: "Database-driven Geant4 Monte-Carlo simulation framework (GEMC)"
330+
readme-filepath: ./ci/dockerhub/README.md
331+
277332
final:
278333
name: Deploy - summary
279334
if: >-

ci/dockerhub/README.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# GEMC — Geant4 Monte-Carlo simulation framework
2+
3+
**GEMC** (the *GEant Monte-Carlo*) is a database-driven [Geant4](https://geant4.web.cern.ch) application for
4+
detector and radiation-transport simulations. Detector descriptions live outside the compiled C++ application:
5+
you define geometry, materials, optics, sensitive detectors, and run configuration in Python or data files, and
6+
GEMC loads them at run time to execute the full Geant4 pipeline.
7+
8+
These images ship a ready-to-run GEMC development environment — the compiled `gemc` simulator, the bundled
9+
`pygemc` Python tools, and all Geant4 dependencies — so you can simulate without managing a local Geant4 build.
10+
11+
- 🏠 Homepage & docs: https://gemc.github.io/home/
12+
- 📦 Source: https://github.com/gemc/src
13+
- 🐍 Python API (`pygemc`): https://pypi.org/project/pygemc/
14+
15+
> These images are mirrored from the canonical GitHub Container Registry build at `ghcr.io/gemc/src`.
16+
> The two registries carry identical content and tags; use whichever is closer to your infrastructure.
17+
18+
<br/>
19+
20+
## Quick start
21+
22+
```shell
23+
docker run -it --rm -v "$PWD":/work docker.io/maureeungaro/gemc:dev-ubuntu-24.04 bash
24+
```
25+
26+
Inside the container the `gemc` binary and the `pygemc` Python environment are already on the `PATH`:
27+
28+
```shell
29+
gemc -v # version sanity check
30+
gemc-system-template -s counter # scaffold a minimal detector system
31+
cd counter && ./counter.py # build geometry (writes gemc.db)
32+
gemc counter.yaml # run the simulation
33+
gemc-analyzer counter_t0_digitized.csv totEdep --kind csv --bins 50
34+
```
35+
36+
<br/>
37+
38+
## Supported tags
39+
40+
Tags follow the pattern `<gemc-version>-<os>-<os-version>` and are multi-architecture
41+
(`linux/amd64` + `linux/arm64`, except Arch Linux which is `amd64`-only).
42+
43+
| OS | Example tag | Architectures |
44+
|------------|----------------------|------------------|
45+
| AlmaLinux | `dev-almalinux-10` | `amd64`, `arm64` |
46+
| Arch Linux | `dev-archlinux-latest` | `amd64` |
47+
| Debian | `dev-debian-13` | `amd64`, `arm64` |
48+
| Fedora | `dev-fedora-44` | `amd64`, `arm64` |
49+
| Ubuntu | `dev-ubuntu-24.04` | `amd64`, `arm64` |
50+
| Ubuntu | `dev-ubuntu-26.04` | `amd64`, `arm64` |
51+
52+
The `dev` prefix tracks the latest `main`; versioned release tags follow the same scheme.
53+
54+
<br/>
55+
56+
## What's inside
57+
58+
- The compiled `gemc` Geant4 simulator
59+
- The bundled `pygemc` Python environment (geometry building, PyVista preview, VTK.js export, analyzer)
60+
- Geant4, CLHEP, Xerces-C, and the supporting toolchain
61+
62+
Key GEMC features:
63+
64+
- Python-first detector definition through `pygemc`
65+
- Geometry/material storage in SQLite or GEMC ASCII databases; imports from GDML and CAD meshes
66+
- Built-in sensitive-detector digitizations: `flux`, `gPhotonDetector`, `dosimeter`, `particle_counter`
67+
- C++ plugin infrastructure for custom digitization, fields, particle readers, and output streamers
68+
- Output streamers for ASCII, CSV, JSON, JLAB SRO, and optional ROOT
69+
- Magnetic-field support, including a built-in multipole field plugin
70+
- Interactive Geant4/Qt visualization and off-screen image generation
71+
72+
<br/>
73+
74+
## Mounting your work
75+
76+
The examples above mount the current directory at `/work`. Use a bind mount to keep geometry scripts, steering
77+
cards, and output on the host:
78+
79+
```shell
80+
docker run -it --rm -v "$PWD":/work -w /work docker.io/maureeungaro/gemc:dev-ubuntu-24.04 bash
81+
```
82+
83+
For the interactive Geant4/Qt GUI and Apptainer recipes, see the
84+
[installation page](https://gemc.github.io/home/installation/).
85+
86+
<br/>
87+
88+
## Documentation & support
89+
90+
- [Installation guide](https://gemc.github.io/home/installation/)
91+
- [Quickstart tutorial](https://gemc.github.io/home/documentation/quickstart/)
92+
- [Examples gallery](https://gemc.github.io/home/examples/)
93+
- [Issue tracker](https://github.com/gemc/src/issues)
94+
95+
<br/>
96+
97+
## Citation
98+
99+
If you use GEMC in scientific work, please cite:
100+
101+
> M. Ungaro, "Geant4 Monte-Carlo (GEMC) A database-driven simulation program," EPJ Web of Conferences 295,
102+
> 05005 (2024). https://doi.org/10.1051/epjconf/202429505005
103+
104+
## License
105+
106+
GEMC is distributed under the project license (see the [source repository](https://github.com/gemc/src/blob/main/LICENSE.md)).
107+
The images also contain separately licensed third-party components, including Geant4, CLHEP, Qt, ROOT, SQLite, and
108+
Assimp.

gemc.cc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,13 @@ int main(int argc, char* argv[]) {
4747
// random engine set by options
4848
gemc::start_random_engine(gopts, log);
4949

50+
// GUI runs are interactive and short-lived; use the serial run manager to avoid repeatedly
51+
// starting task workers from the Qt event loop on each Run button click.
52+
const auto runManagerType = gui ? G4RunManagerType::SerialOnly : G4RunManagerType::Default;
53+
5054
// init geant4 run manager with then number of threads coming from options. always fails if unavailable
5155
auto runManager = std::unique_ptr<G4RunManager>(
52-
G4RunManagerFactory::CreateRunManager(G4RunManagerType::Default, true, nthreads));
56+
G4RunManagerFactory::CreateRunManager(runManagerType, true, nthreads));
5357

5458
// Pre-load streamer plugins before Geant4 creates worker threads. Sanitized Linux
5559
// builds can fail late dlopen() calls from workers with static TLS exhaustion.

gemc/eventDispenser/eventDispenser.cc

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,11 +14,22 @@
1414
#include <utility>
1515

1616
// geant4
17+
#include "G4GeometryManager.hh"
1718
#include "G4UImanager.hh"
1819
#include "G4RunManager.hh"
1920

2021
using namespace std;
2122

23+
namespace {
24+
void closeOpenGeometryBeforeBeamOn(const std::shared_ptr<GLogger>& log) {
25+
auto* geometryManager = G4GeometryManager::GetInstanceIfExist();
26+
if (!geometryManager || geometryManager->IsGeometryClosed()) { return; }
27+
28+
log->info(1, "Geometry is open before BeamOn; closing it before event processing.");
29+
geometryManager->CloseGeometry();
30+
}
31+
}
32+
2233
// Constructor summary:
2334
// - Reads configuration (number of events, run number, optional run-weight file).
2435
// - Builds runWeights/runEvents/listOfRuns when weights are provided.
@@ -188,6 +199,7 @@ int EventDispenser::processEvents() {
188199
// Dispatch all events for this run in a single call.
189200
// The command string is a standard Geant4 UI command: \c /run/beamOn <N>.
190201
log->info(1, "Processing ", nevents, " events in one go");
202+
closeOpenGeometryBeforeBeamOn(log);
191203
g4uim->ApplyCommand("/run/beamOn " + to_string(nevents));
192204
// Take the screenshot after BeamOn returns. At this point G4VisManager::EndOfRun()
193205
// has already joined the vis subthread (ARM64 offset 0xa35f8: bl thread::join), so
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# x y z field-query points
2+
0*cm 0*cm 0*cm
3+
10*cm, 0*cm, 0*cm

gemc/gfields/gfield_options.cc

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,37 @@ std::vector<GFieldDefinition> get_GFieldDefinition(const std::shared_ptr<GOption
153153
gfield_defs.push_back(gfield_def);
154154
}
155155

156+
// Generic plugin-backed fields:
157+
// Each "gfields" entry names a field, selects a plugin through "type", and carries an arbitrary
158+
// set of scalar parameters that are forwarded verbatim to the plugin via field_parameters. This
159+
// lets external plugins (e.g. clas12 mapped fields) be configured without changing this parser.
160+
auto gfields_node = gopts->getOptionNode("gfields");
161+
for (auto gfields_item : gfields_node) {
162+
GFieldDefinition gfield_def = GFieldDefinition();
163+
164+
// Core identity and integration configuration (the schema-defined keys).
165+
gfield_def.name = gopts->get_variable_in_option<std::string>(
166+
gfields_item, "name", goptions::NODFLT);
167+
gfield_def.type = gopts->get_variable_in_option<std::string>(
168+
gfields_item, "type", goptions::NODFLT);
169+
gfield_def.integration_stepper = gopts->get_variable_in_option<std::string>(
170+
gfields_item, "integration_stepper", GFIELD_DEFAULT_INTEGRATION_STEPPER);
171+
gfield_def.minimum_step = gutilities::getG4Number(gopts->get_variable_in_option<std::string>(
172+
gfields_item, "minimum_step", GFIELD_DEFAULT_MINIMUM_STEP));
173+
174+
// Every remaining (scalar) key is forwarded to the plugin as a string parameter.
175+
// Nested maps/sequences are not supported here: plugin parameters must be scalar values.
176+
for (auto it = gfields_item.begin(); it != gfields_item.end(); ++it) {
177+
auto key = it->first.as<std::string>();
178+
if (key == "name" || key == "type" || key == "integration_stepper" || key == "minimum_step") {
179+
continue;
180+
}
181+
gfield_def.add_map_parameter(key, it->second.as<std::string>());
182+
}
183+
184+
gfield_defs.push_back(gfield_def);
185+
}
186+
156187
return gfield_defs;
157188
}
158189

@@ -184,6 +215,22 @@ GOptions defineOptions() {
184215
};
185216
goptions.defineOption("gmultipoles", "define the e.m. gmultipoles", gmultipoles, help);
186217

218+
std::string gfields_help;
219+
gfields_help = "Adds a generic, plugin-backed electromagnetic field to the simulation. \n \n";
220+
gfields_help += "The 'type' selects the plugin shared library named gfield<type>Factory. \n";
221+
gfields_help += "Any additional scalar keys are forwarded verbatim to that plugin as string \n";
222+
gfields_help += "parameters (so the plugin alone decides which parameters it understands). \n \n";
223+
gfields_help += "Mandatory keys: name, type. \n \n";
224+
gfields_help += "Example (clas12 binary mapped field from the clas12-systems plugin): \n";
225+
gfields_help += "-gfields=\"[{name: clas12, type: clas12bin, solenoid: solenoid_map, torus: torus_map}]\"\n";
226+
std::vector<GVariable> gfields = {
227+
{"name", goptions::NODFLT, "Field name (unique key used by GMagneto maps)"},
228+
{"type", goptions::NODFLT, "Field type; selects the plugin shared library gfield<type>Factory"},
229+
{"integration_stepper", GFIELD_DEFAULT_INTEGRATION_STEPPER, "Geant4 integration stepper name (string)"},
230+
{"minimum_step", GFIELD_DEFAULT_MINIMUM_STEP, "Minimum step for the G4ChordFinder (Geant4 length units)"}
231+
};
232+
goptions.defineOption("gfields", "define a generic plugin-backed e.m. field", gfields, gfields_help);
233+
187234
goptions.defineOption(
188235
GVariable("fieldAt", UNINITIALIZEDSTRINGQUANTITY, "query all configured fields at x y z"),
189236
"Evaluate all configured electromagnetic fields at one absolute coordinate.\n \n"

releases/0.4.md

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,17 @@
22

33
This version includes:
44

5-
5+
- Field query options to print configured magnetic-field values at absolute coordinates.
66

77

88

99
<br/>
1010

1111
# Release notes
1212

13-
13+
- Added `-fieldAt="x y z"` and `-fieldMapPoints=file.txt` to evaluate configured gfield maps at
14+
absolute coordinates with unit-bearing input, printing coordinates and field components with
15+
`G4BestUnit`.
1416

1517
<br/>
1618

@@ -22,13 +24,13 @@ This version includes:
2224

2325
## Examples
2426

25-
27+
- Added gfield example coverage for direct coordinate queries and ASCII point-list input.
2628

2729
<br/>
2830

2931
## Tests
3032

31-
33+
- Added gfield tests for `-fieldAt` and `-fieldMapPoints` query workflows.
3234

3335
<br/>
3436

@@ -77,3 +79,8 @@ Both x86_64 and ARM64 platforms are supported.
7779
<br/>
7880

7981
## Detailed list of changes and fixes
82+
83+
- Added gfield query mode: `-fieldAt="x y z"` accepts one unit-bearing coordinate triplet, while
84+
`-fieldMapPoints=file.txt` reads one `x y z` triplet per non-comment ASCII line. Query mode loads
85+
the configured field definitions, prints one result row per field and point, and exits before normal
86+
simulation setup.

0 commit comments

Comments
 (0)