Derived Fields & add_field

Mera computes a large catalogue of derived quantities on demand through getvar — temperature, sound speed, Mach number, cylindrical/spherical velocities, specific angular momentum, Jeans length, kinetic/thermal energy, and many more. You ask for them by name and Mera builds them from the raw stored variables:

gas = gethydro(getinfo(output, path))

getvar(gas, :T,    :K)          # temperature
getvar(gas, :mach)              # Mach number
getvar(gas, :ekin, :erg)        # kinetic energy
getvar(gas, [:vr_cylinder, :vϕ_cylinder], :km_s; center=[:bc])

These derived names also work everywhere getvar is used internally — in projection, profile, phase, and friends.

Exact formulas

For the full set of formulas behind every derived quantity (thermodynamics, velocities, angular momentum, Mach numbers, Jeans/collapse, gravity) and the aggregate statistics (msum, center_of_mass, bulk_velocity, wstat), see How Quantities Are Computed.

Conventions for selected quantities

A few derived quantities carry physical assumptions worth stating explicitly:

  • Jeans length :jeanslength uses the standard form $λ_J = c_s\,\sqrt{\dfrac{3π}{32\,G\,ρ}}$ (and :jeansmass/:jeansnumber follow from it). This is one of several Jeans-length conventions in the literature; factors of order unity differ between them.
  • Magnetosonic Mach numbers :mach_alfven, :mach_fast, :mach_slow require the magnetic-field components :bx,:by,:bz (an MHD run) and error otherwise. The B field is taken in RAMSES code units and converted to Gaussian-CGS internally (Alfvén speed $v_A = B/\sqrt{4πρ}$); fast/slow use $v_{f}=\sqrt{c_s^2+v_A^2}$ and the isotropic $v_{s}=c_s v_A/\sqrt{c_s^2+v_A^2}$. All three are dimensionless. (Because they need the field components, getvar_requirements lists :bx,:by,:bz,:rho among their dependencies.)
  • Escape speed :escape_speed $= \sqrt{-2φ}$ is defined only where the potential $φ<0$ (bound); unbound cells ($φ≥0$, possible near boundaries) are clamped to 0 rather than erroring.
  • Cosmological-only quantities — :overdensity/:delta (hydro) and :age-relatives :formation_time/:formation_redshift/:zform (particles) — are defined only for cosmological runs and error on non-cosmological output.

The dependency registry

Each derived quantity knows which raw variables it is built from. That graph is queryable:

getvar_requirements(:hydro, :ekin)        # [:rho, :vx, :vy, :vz]
getvar_requirements(:hydro, :jeanslength) # [:rho, :p]
getvar_requirements(:hydro, [:sd, :T])    # [:rho, :p]

This is what lets the one-call verbs read only what they need instead of the whole hydro state. project(info, :sd) reads just :rho; project(info, :sd; direction=:edgeon) also pulls the velocities required to orient the disk; quicklook reads only :rho and :p. When a requirement cannot be resolved (e.g. a custom field whose dependency is not stored in that output) the readers safely fall back to reading everything.

Adding your own field: add_field

Register a custom derived field once and it behaves like any built-in quantity — including inside projection and profile. This is the equivalent of yt's add_field.

using Mera

# velocity magnitude squared, built from the velocity components
add_field(:vmag2, (obj, deps) -> deps[:vx].^2 .+ deps[:vy].^2 .+ deps[:vz].^2;
          depends_on = [:vx, :vy, :vz])

getvar(gas, :vmag2)             # works in getvar …
projection(gas, :vmag2)         # … and in projection …
profile(gas, :r_cylinder, :vmag2)   # … and in profile / phase

The compute kernel

compute(dataobject, deps):

  • dataobject — the data object the field is being evaluated on.
  • deps — a Dict{Symbol,Vector} holding the arrays named in depends_on, already evaluated with the same centering and masking as the outer getvar call.
  • Return the field in code units; Mera applies the requested unit (or this field's default unit) for you.

Dependencies may be raw variables, other built-in derived quantities, or even other user fields — they are resolved recursively:

# a field built on top of a built-in derived quantity (:cs, the sound speed)
add_field(:mach_custom, (o, d) -> sqrt.(d[:vx].^2 .+ d[:vy].^2 .+ d[:vz].^2) ./ d[:cs];
          depends_on = [:vx, :vy, :vz, :cs])

A registered field is a first-class citizen: it flows through getvar, projection, profile and the rest, with its dependencies read and resolved automatically. For example, once :mach_custom is registered, projection(gas, :mach_custom) just works:

A user-defined field projected like any built-in. `add_field(:mach_custom, …)` registers the local Mach number ℳ = |v|/c_s on top of the built-in sound speed `:cs`; `projection(gas, :mach_custom)` then renders it — supersonic disk gas (red) over the subsonic halo (blue).

Units

Give a field a default unit (it must be a field of info.scale, or :standard for code units). A unit passed at call time overrides the default:

add_field(:rho_msun_pc3, (o, d) -> d[:rho]; depends_on = [:rho], unit = :Msol_pc3)
getvar(gas, :rho_msun_pc3)               # returns code-unit ρ scaled by info.scale.Msol_pc3
getvar(gas, :rho_msun_pc3, :standard)    # call-time unit override → code units

Other data types

By default fields are registered for hydro. Register for other kinds (or several at once) with datatypes:

add_field(:speed, (o, d) -> sqrt.(d[:vx].^2 .+ d[:vy].^2 .+ d[:vz].^2);
          depends_on = [:vx, :vy, :vz], datatypes = [:hydro, :particle])

Valid kinds: :hydro, :gravity, :rt, :particle, :clump.

Managing registered fields

list_fields(:hydro)                 # names you added for hydro (custom only)
list_fields(:hydro; builtin=true)   # built-in derived fields ∪ your custom ones, sorted
field_info(:vmag2)                  # (; compute, depends_on, unit, description)
delete_field(:vmag2)                # remove it (delete_field(name; datatypes=:all) by default)

list_fields(kind; builtin=true) is the quickest way to discover what you can ask getvar for on a given data type — it returns the dependency-registry built-ins together with any fields you registered. It covers most but not every built-in quantity (a few specialised fields are computed directly in getvar); for the complete human-readable catalogue call getvar() with no arguments.

The default (builtin=false) lists only the fields you registered, so it starts empty and grows as you add_field (and shrinks again on delete_field):

list_fields(:hydro)                 # builtin=false (default): custom fields only — none yet
Symbol[]
add_field(:speed2, (o, d) -> d[:vx].^2 .+ d[:vy].^2 .+ d[:vz].^2; depends_on=[:vx, :vy, :vz])
list_fields(:hydro)                 # the field you just added now appears
1-element Vector{Symbol}:
 :speed2
delete_field(:speed2)
list_fields(:hydro)                 # removed again → back to empty
Symbol[]

With builtin=true the same call instead returns the full catalogue. The lists below are generated live from the registry at doc-build time, so they always match the installed version. Hydro:

list_fields(:hydro; builtin=true)
73-element Vector{Symbol}:
 :T
 :T_rt
 :Temp
 :Temperature
 :beta
 :bmag
 :bx
 :by
 :bz
 :cellsize
 ⋮
 :vz2
 :vθ_sphere
 :vϕ_cylinder
 :vϕ_cylinder2
 :vϕ_sphere
 :x
 :y
 :z
 :ϕ

Gravity, RT, particle, clump (same call, different kind):

list_fields(:gravity;  builtin=true)
17-element Vector{Symbol}:
 :a_magnitude
 :ar_cylinder
 :ar_sphere
 :aθ_sphere
 :aϕ_cylinder
 :aϕ_sphere
 :cellsize
 :escape_speed
 :gravitational_redshift
 :r_cylinder
 :r_sphere
 :specific_gravitational_energy
 :volume
 :x
 :y
 :z
 :ϕ
list_fields(:particle; builtin=true)
31-element Vector{Symbol}:
 :age
 :ekin
 :formation_redshift
 :formation_time
 :h
 :hx
 :hy
 :hz
 :l
 :lr_cylinder
 ⋮
 :vr_sphere
 :vx2
 :vy2
 :vz2
 :vθ_sphere
 :vϕ_cylinder
 :vϕ_sphere
 :zform
 :ϕ
(rt = list_fields(:rt; builtin=true), clump = list_fields(:clump; builtin=true))
(rt = [:cellsize, :r_cylinder, :r_sphere, :volume, :x, :y, :z, :ϕ], clump = [:ekin, :mass, :v, :x, :y, :z])
Registry scope

Registered fields live for the current Julia session (they are not persisted to disk). Put your add_field calls in a startup script or at the top of your analysis to make them available every run.

Registered fields also work as quantities in First-Look Reports cards — the report reads only the dependencies your field declares.

Custom units

A field's unit can be an existing info.scale field, :standard, a plain number (a literal code→display factor), or a custom unit you register with add_unit. Registered units work everywhere a unit is accepted — including getvar(obj, var, unit) for built-in quantities:

add_unit(:Msun_per_century, 1e-2)               # 1 code-unit value × 1e-2
add_field(:mdot, (o,d) -> d[:rho]; depends_on=[:rho], unit=:Msun_per_century)
getvar(gas, :mass, :Msun_per_century)           # also applies to built-in fields
list_units();  delete_unit(:Msun_per_century)

Inspecting dependencies

field_dependencies(:hydro, :ekin)   # (; direct=[:mass,:v], raw=[:rho,:vx,:vy,:vz])
field_tree(:hydro, :mach)           # prints the dependency tree down to raw leaves
mach
├─ v
│  ├─ vx  (raw)
│  ├─ vy  (raw)
│  └─ vz  (raw)
└─ cs
   ├─ p  (raw)
   └─ rho  (raw)

See also

Registered fields are used throughout Mera: getvar computes them, projection and profile read only the dependencies they need (via getvar_requirements), and the First Look (quicklook / report) verbs benefit from the same needs-based reading. See also Star-Formation Rate and Clump Finding for fields used in analysis.

API

Mera.add_fieldFunction
add_field(name::Symbol, compute::Function; depends_on=Symbol[], datatypes=:hydro,
          unit::Symbol=:standard, description::String="")

Register a user-defined derived field that then behaves like any built-in getvar quantity — it works in getvar, and therefore in projection, profile, phase, etc.

  • compute(dataobject, deps) — your kernel. deps is a Dict{Symbol,Vector} holding the arrays of depends_on (already centered / masked consistently). Return the field in code units; the requested unit (or this field's default unit) is applied for you.
  • depends_on — the variables your kernel needs (built-in or other user fields). These are also recorded in the dependency graph so getvar_requirements (and the read-only-what-you-need logic in project/quicklook) cover your field.
  • datatypes — a kind symbol or collection of them: :hydro, :gravity, :rt, :particle, :clump.
  • unit — default unit symbol (must be a field of info.scale, or :standard).
add_field(:vmag2, (o, d) -> d[:vx].^2 .+ d[:vy].^2 .+ d[:vz].^2; depends_on=[:vx,:vy,:vz])
getvar(gas, :vmag2)
projection(gas, :vmag2)

See also delete_field, list_fields.

Mera.delete_fieldFunction
delete_field(name::Symbol; datatypes=:all)

Remove a previously add_field-registered field. datatypes=:all (default) removes it from every kind; otherwise pass a kind symbol or collection.

Mera.list_fieldsFunction
list_fields(kind::Symbol=:hydro; builtin::Bool=false) -> Vector{Symbol}

The registered derived-field names for a data-type kind (:hydro, :gravity, :rt, :particle, :clump), sorted.

By default only the user-added fields (registered with add_field) are returned — this is the back-compatible behaviour. With builtin=true the result also includes the built-in derived quantities known to the dependency registry (FIELD_DEPS[kind]), so you get a single combined list of everything resolvable for that kind:

list_fields(:hydro)                 # only the fields you added
list_fields(:hydro; builtin=true)   # built-in registry fields ∪ your custom fields

Note: builtin=true reflects the dependency registry, which covers most but not every built-in quantity (a few specialised fields, e.g. some RT-ionization variables, are computed directly in getvar without a registry entry). For the full human-readable catalogue call getvar() with no arguments.

Mera.field_infoFunction
field_info(name::Symbol; kind::Symbol=:hydro)

The registration record (; compute, depends_on, unit, description) for a user field, or nothing if it isn't registered for that kind.

Mera.field_dependenciesFunction
field_dependencies(kind::Symbol, var::Symbol) -> (; direct, raw)

Inspect a derived field's dependencies for data-type kind: direct are its immediate dependencies (raw or derived, as declared), raw is the transitive set of physical stored variables it ultimately needs (same as getvar_requirements). Works for built-in and add_field-registered quantities.

Mera.field_treeFunction
field_tree(kind::Symbol, var::Symbol; io=stdout)

Pretty-print the dependency tree of a derived field down to its raw leaves (cycle-safe).

Mera.add_unitFunction
add_unit(name::Symbol, factor::Real)

Register a custom unit: a value in code units is multiplied by factor to convert to this unit. The name then works anywhere a unit symbol is accepted — in add_field (as the field's default unit), and in getvar(obj, var, name).

add_unit(:Msun_per_yr, 1.0)                      # e.g. for an SFR-like custom field
add_field(:mdot, (o,d)->d[:rho]; depends_on=[:rho], unit=:Msun_per_yr)

See also delete_unit, list_units.

Mera.getvar_requirementsFunction
getvar_requirements(kind::Symbol, vars) -> Vector{Symbol}

The sorted set of physical stored variables that must be read to compute vars (a Symbol or a collection), with always-present geometry leaves (:cx/:cy/:cz, :level, :x/:y/:z, :cellsize, :volume, …) removed. Unknown/custom symbols are returned as-is (callers can detect these and fall back to reading everything).

getvar_requirements(:hydro, :ekin)        # [:rho, :vx, :vy, :vz]
getvar_requirements(:hydro, [:sd, :T])    # [:rho, :p]   (:sd is an alias of surface density → :rho)