Current Persistent Formats
The current source owners are the format specification. Odin readers and the Python validators accept one exact current shape, reject malformed or unknown fields, and do not provide alternate readers or compatibility migrations. There is no general format-version field.
Hashes are SHA-256. Paths stored in Project records are Project-relative unless the format explicitly records an external Project descriptor or solver path. Writes are staged/atomic and canonical records are non-overwriting.
Project and catalog
Owned by project.odin and project_storage.odin.
<project-directory>/<descriptor-name>.ramal-project
projects/project-catalog.json
The Project descriptor has exactly:
project_id, display_name, phits_launcher, multiplier_file
The catalog registry has exactly:
external_paths: string[]
The descriptor identifies one Project workspace. phits_launcher and
multiplier_file refer to the external PHITS installation; RAMAL does not copy
that installation into the repository. Research protocols are researcher-owned
and are not selected or enforced by the Project record.
Design and PHITS input
Owned by design.odin, design_phits.odin, phits.odin, and
phits_document.odin.
designs/<design-id>/
<design-id>.ramal-design
<design-id>.inp
The JSON Design record owns RAMAL-managed input plus the editable result-field
setup and target. The sibling .inp is derived solver input: ordinary PHITS
text with every $ ramal:* metadata line removed. The display name comes from
[Title] in the record. Result-field setup and target data stay in the JSON
record; they are not emitted as PHITS comments.
When a Design uses the reusable one-dimensional industrial scan source, its
managed [Source] block contains exactly one strict metadata line, for example:
$ ramal:source-scan enabled=1 axis=x min_cm=-50 max_cm=50 fwhm_cm=2 support_diameter_cm=3 positions=101 profile=uniform
The fields are the enabled flag, scan axis (x or y), absolute centre range
in cm, local Gaussian FWHM in cm, RAMAL finite support diameter/cutoff in cm,
odd quadrature position count (3..500), and profile (uniform). The GUI
round-trips these values from this line; it never reconstructs scan intent by
examining expanded <source> subsections. A valid scanned source is a
normally directed s-type = 13 Gaussian on one z plane. Its generated PHITS
card contains one <source> subsection per position. Uniform continuous-line
nodes use normalized trapezoidal weights: h = 1/(N-1), endpoints h/2,
interior nodes h, with the sum exactly 1; totfact = 1 preserves
normalization per primary source electron. PHITS does not provide a native
finite radial cutoff for s-type = 13; the support
field is therefore RAMAL-owned preview/validation metadata, not an invented
solver parameter.
The strict importer rejects malformed or duplicated scan metadata instead of
guessing from solver-expanded source sections. design_solver_input removes
the metadata line when producing a solver-clean Design or immutable Case input,
but keeps the generated multi-source PHITS syntax. The current source API
performs generic range and source-direction checks; exact containment against a
finite converter face remains a project verification gate until converter
ownership is exposed to the reusable geometry API.
Scene_State is in-memory editing state, not a persisted Project record.
Case
Owned by case.odin.
cases/<case-id>/
<case-id>.ramal-case
input.inp
jobs/
The .ramal-case document has exactly:
project_id, case_id, display_name, source_kind, source_design_id,
created_at_utc, input_path, ramal_input, result_contract
source_kind is currently design. result_contract contains:
target, normalization_mode, particles, required_roles, region_reports
target declares its Project-owned ID, quantity, output unit and normalization,
intrinsic unit and normalization, PHITS multiplier ID, source-to-output scale,
and primary particle. The editable Design record separately retains setup mode
(custom, guided-h10, or guided-icru95) and the target fields; the
immutable Result Contract keeps its existing nine-field target shape and
rechecks the generated multiplier, c1 scale, quantity, and unit. required_roles
contains:
vtk, report, uncertainty_vtk, uncertainty_report
input.inp is the immutable solver-clean physical input. ramal_input retains
the corresponding RAMAL-managed input, including metadata needed by the GUI,
Dataset builder, and Assessment calculations. Case loading proves that removing
all $ ramal:* lines from ramal_input exactly reproduces input.inp, then
re-derives and verifies the immutable Result specification. Run controls belong
to an Attempt and are not written back into the Case.
Optional Design Case Controls
A Design may declare a small, strict set of optional numeric Case Controls in PHITS comments. The Design remains editable; the controls describe which Design values may be specialized when one immutable Case is published. The initial supported operations are exactly:
$ ramal:case-control feature=energy-mev kind=source-energy min=0.2 max=8.0
$ ramal:case-control feature=downstream-concrete-cm kind=rpp-thickness inner=101 outer=102 axis=y side=min min=20 max=60
The numeric bounds are illustrative; the active research procedure owns its predefined physical ranges.
source-energy requires MeV on the matching numeric feature declaration and
sets PHITS source energy. rpp-thickness requires cm and adjusts one min
or max face of an outer RPP relative to the corresponding face of an
enclosing inner RPP. Multiple target lines may share one feature and value,
but every target must own a distinct physical face and remain a positive,
enclosing RPP geometry. Field names and order are exact; unknown, duplicate,
conflicting, non-finite, missing-feature, missing-surface, non-RPP, and
physically inconsistent declarations fail closed.
Creating a Case with no values freezes the current Design values exactly.
Creating one with values requires one finite, in-range value for every
declared control, with no missing, extra, or duplicate feature IDs. RAMAL
specializes an owned temporary Scene_State, generates and merges PHITS from
that temporary scene, rejects material-cell overlaps, and re-imports and
resolves the exact specialized PHITS bytes before publication. The Result
contract is derived from that re-imported input. RAMAL rewrites only the
controlled $ ramal:feature lines to the selected numeric values while
preserving their dataset-compatible three-field shape. The Design scene and
Design input bytes are not mutated.
There is no Variant object, batch-generation path, compatibility reader,
schema-version field, or separate Case provenance document. Study schedules
existing Cases and does not generate new Cases.
Job and Attempt
Owned by job.odin and attempt.odin.
cases/<case-id>/jobs/<job-id>/<job-id>.ramal-job
cases/<case-id>/jobs/<job-id>/attempts/<attempt-id>/
scene.phits.inp
manifest.json
The .ramal-job document has exactly:
project_id, case_id, job_id, display_name, maxcas, maxbch, rseed,
replica_index, execution_target, status, created_at_utc, attempts
execution_target is either local or a safe remote profile ID. It is
persisted with the Job so retries retain their target even if a profile is
temporarily unavailable. The embedded Attempt entries have:
attempt_id, status, phase, execution_input_path, attempt_record_path,
result_id, started_at_utc, ended_at_utc, return_code
Attempt IDs are a0001 through a9999. The strict runner in
tools/run_case_attempt.py accepts:
--prepare-attempt
--execute-prepared-attempt
--verify-succeeded-attempt
--resolve-result-roles
Its manifest.json records the Attempt status and times, executed command,
PHITS version, return code, execution controls, result and region-report roles,
and the paths of files retained in the Attempt. The exact executed input is
scene.phits.inp; logs and outputs remain beside it.
Remote transport does not alter this Job, Attempt, manifest, or Result schema. Before terminal transfer it keeps a strict operational sidecar adjacent to the Attempt, never inside it:
cases/<case-id>/jobs/<job-id>/attempts/.<attempt-id>.remote.json
The sidecar stores the selected profile, execution identity, recovery paths,
tmux session, current progress, terminal state, and diagnostics. It is
operational state, not evidence, and is not included in the Attempt manifest.
A remote terminal Attempt
contains the normal manifest plus transport files such as phits.in,
remote-launch.sh, remote_phits_worker.py, phits.out, and run.log.
Remote profiles are external application configuration, not Project data:
~/Library/Application Support/RAMAL-EBX/remote-profiles.json
The remote profile document has exactly profiles and no format-version field.
Each profile supplies its safe ID, display name, SSH host/user, remote
root, Python/tmux/MPI/PHITS paths, hostfile, process count, and optional
ssh_port, ssh_host_key_alias, and mpi_environment. ssh_port defaults to
22; ssh_host_key_alias defaults to ssh_host and preserves host-key
verification when a trusted local TCP forward uses 127.0.0.1.
An AnyDesk forward tunnel with local port 2222 and remote destination
10.10.180.64:22 can be represented as a second profile without changing the
ordinary direct profile:
{
"id": "fz-mm1-anydesk",
"display_name": "fz-mm1 MPI through AnyDesk",
"exclusive_cluster": true,
"ssh_user": "faiz",
"ssh_host": "127.0.0.1",
"ssh_port": 2222,
"ssh_host_key_alias": "fz-mm1",
"remote_root": "/Users/faiz/work/ramal-ebx",
"python_path": "/usr/bin/python3",
"tmux_path": "/opt/homebrew/bin/tmux",
"mpirun_path": "/opt/homebrew/bin/mpirun",
"hostfile_path": "/Users/faiz/hostfile",
"phits_path": "/Users/faiz/phits/phits_MacGfort_MPI",
"mpi_processes": 32
}
Result
Owned by result.odin.
cases/<case-id>/jobs/<job-id>/<result-id>.ramal-result
The .ramal-result document has exactly:
project_id, case_id, job_id, result_id, attempt_id, status, completed_at_utc
The status is completed. Opening a Result validates its canonical Case, Job,
Attempt, manifest, result roles, output fields, and exact execution input
before replacing the immutable Analyze scene.
Study and Assessment
Owned by study.odin and assessment.odin.
studies/<study-id>/<study-id>.ramal-study
studies/<study-id>/assessments/<assessment-id>.ramal-study-assessment
Study fields are exactly:
project_id, study_id, display_name, question, created_at_utc,
reference_case_id, inclusion_criteria, domain, evaluation_regions, members
reference_case_id is empty when no reference is required. The inclusion
criteria contain max_relative_error, min_reportable_fraction, and
min_reportable_voxel_count. The domain contains min_cm, max_cm, and
mesh_count. Evaluation regions and members retain their exact spatial and
Case/Job/replica bindings.
Assessment fields are exactly:
project_id, study_id, assessment_id, created_at_utc, status, members
Assessment member status and regional metrics are persisted. Status is
meets-criteria or does-not-meet-criteria when calculation succeeds; each
regional metrics object records meets_criteria. Summary counts are derived
from member statuses. Result and Attempt integrity is re-verified from their
canonical records.
Case feature declaration
Each Dataset-selected feature must occur exactly once in the immutable Case input:
$ ramal:feature id=energy-mev value=5.0 unit=MeV
$ ramal:feature id=converter-state value=absent unit=category
Continuous declarations use finite numeric values. Binary declarations use one
of the two category IDs from the Dataset feature definition. The builder maps
the category to index 0 or 1 for numeric arrays. A Case may retain additional
feature declarations that a particular Dataset does not learn. This permits a
Dataset to omit a constant feature without erasing the Case's physical
provenance.
Feature definition
Every feature object has exactly these keys:
{
"feature_id": "energy-mev",
"display_name": "Incident electron energy",
"kind": "continuous",
"unit": "MeV",
"minimum": 1.0,
"maximum": 10.0,
"categories": []
}
Rules:
kindis exactlycontinuousorbinary.- A continuous feature has an empty
categorieslist, a physical unit, and finitemaximum > minimum. - A binary feature has
unit: "category",minimum: 0,maximum: 1, and exactly two distinct valid record IDs incategories. - An illustrative binary feature is:
{
"feature_id": "converter-state",
"display_name": "Converter state",
"kind": "binary",
"unit": "category",
"minimum": 0,
"maximum": 1,
"categories": ["absent", "present"]
}
Example ranges and categories describe the schema only. A research procedure must define its own physically justified values.
Dataset specification
Owned by tools/build_dataset.py and indexed by research_index.odin.
datasets/<dataset-id>/<dataset-id>.ramal-dataset
The specification has exactly these top-level keys:
{
"dataset_id": "dataset-001",
"display_name": "Reportable dose fields",
"study_id": "study-001",
"assessment_id": "assessment-0001",
"feature_definitions": [],
"samples": [],
"partitioning_method": {},
"minimum_common_coverage": 0.95
}
Each specification sample has exactly:
{
"configuration_id": "configuration-0001",
"member_id": "member-0001",
"split": "train",
"feature_values": [5.0, 0]
}
feature_values are numeric in feature-definition order. A binary value must be exactly 0 or 1.
Partitioning method
The method has exactly these keys:
{
"algorithm": "declared-whole-configuration",
"seed": 17,
"paired_feature_id": "converter-state"
}
Use an empty paired_feature_id for ordinary samples. When it names a binary
feature, derive each group from all other feature values. Require exactly one
sample for each binary category, and require all group members to use the same
partition. Count readiness by complete groups: at least two training groups,
one model-selection group, and one final-test group.
The seed records the declared partition traceability. It is not permission to randomly reassign samples during building or verification.
Dataset manifest
The published manifest has exactly:
project_id, dataset_id, display_name, created_at_utc, study,
feature_definitions, target, grid, spatial_input, partitioning_method, samples,
artifacts
The arrays remain numeric:
features.npy
dose.npy
dose-rate.npy
current-multiplier.npy
relative-error.npy
splits.npy
evaluation-mask.npy
common-reportable-mask.npy
x-coordinates.npy
y-coordinates.npy
z-coordinates.npy
material-fractions.npy
dominant-material-id.npy
mass-density-g-cm3.npy
air-fraction.npy
signed-distance-cm.npy
region-masks.npy
primary-evaluation-mask.npy
dose.npy contains the scientific target in the intrinsic unit and
normalization declared by target; dose-rate.npy preserves the PHITS output
field in its declared output unit and normalization. current-multiplier.npy
stores the exact positive set: c1[...] scale connecting them. These filenames
are stable storage roles; the Dataset target metadata, not the names, defines
the physical quantity.
Spatial Cell arrays use canonical [z, y, x] order; material fractions have a
leading sample and material channel, and region masks have leading sample and
region channels. The current exact exporter supports baked, untransformed RPP
room Cells and fails closed on unsupported spatial geometry.
The manifest binds every sample to its procedure, immutable Case specification,
Result, Attempt manifest, feature values, partition, and current multiplier.
Every verification regenerates the spatial tensors from the immutable
input/specification and compares them exactly. The common-reportable-mask
remains the intersection of positive reportable voxels across selected
configurations. An unscored value is never rewritten as zero.
Model manifest
Owned by tools/model_pipeline.py and indexed by research_index.odin.
models/<model-id>/<model-id>.ramal-model
models/<model-id>/evaluations/final-test.ramal-model-evaluation
The Model repeats the exact Dataset feature_definitions and target specification. The finalized training procedure uses model-selection results for candidate selection and keeps the final-test partition untouched until explicit evaluation. Binary feature definitions are retained in the Model so prediction validation and the UI can enforce categories.
Prediction specification and manifest
predictions/<prediction-id>/<prediction-id>.ramal-prediction
predictions/<prediction-id>/verifications/<verification-id>.ramal-prediction-verification
The Prediction specification remains deliberately numeric:
{
"prediction_id": "prediction-001",
"display_name": "No converter at 5 MeV",
"feature_values": [5.0, 0]
}
The backend rejects non-integral binary values, out-of-domain values, wrong feature counts, and mismatched feature order. The UI renders binary features as category combos and continuous features as numeric inputs.
The Prediction manifest records the selected numeric values together with the exact feature IDs and units. The Odin index repeats the same binary-integrality and domain checks and fails closed on invalid prediction records.
Final-test evaluation
For every candidate algorithm, final-test evidence has:
{
"aggregate_metrics": {},
"configuration_metrics": [],
"category_metrics": {
"converter-state": {
"absent": {
"sample_count": 1,
"aggregate_metrics": {},
"configuration_metrics": []
},
"present": {
"sample_count": 1,
"aggregate_metrics": {},
"configuration_metrics": []
}
}
}
}
The exact object is reproduced during verification. Aggregate scores alone are insufficient whenever category-specific performance is part of the claim.
Dataset physical boundary
A Dataset inherits its physical boundary from its immutable Cases and shared Result specification. The current scalar feature declarations do not replace the underlying geometry or source data provenance. The current builder requires every selected Result to share one exact grid and target specification; it does not encode a spatially variable material geometry as an ML input.
Non-record runtime state
Simulation, Study_Run, and ML_Task are transient state. Project, Design,
Case, Job, Attempt, Result, Study, Assessment, Dataset, Model, Prediction, and
Prediction Verification are persisted domain records. Catalogs and
Research_Index are rebuilt runtime indexes.