Analysis Tools
The AnalysisTools module post-processes sampling output into thermodynamic quantities: phase-space weights (ωᵢ), the partition function, internal energy, constant-volume heat capacity (cv), grand-canonical reweighting, and — described below — microcanonical inflection-point analysis of phase transitions.
Microcanonical inflection-point analysis
Nested sampling yields the microcanonical entropy $S(E) = \ln g(E)$ almost for free. Along the contiguous cull index $i$ the enclosed prior volume is $X_i = (K/(K+1))^i$, so $\ln X_i = i\,\ln(K/(K+1))$ and, with the recorded energy ladder $E(i)$,
\[S(E) = i\,\ln\!\frac{K}{K+1} - \ln\left|\frac{dE}{di}\right| + \mathrm{const} ,\]
where derivatives are taken against the dense, uniform $i$-axis (local cubic fits) rather than a binned $\ln g$ in energy space. Following Schnabel et al., Phys. Rev. E 84, 011127 (2011) and Qi & Bachmann, Phys. Rev. Lett. 120, 180601 (2018), phase transitions in finite systems appear as inflection points of the inverse caloric temperature $\beta(E) = dS/dE$, and their order follows from the higher derivatives:
| transition order | independent signature in $S^{(n)}(E)$ |
|---|---|
| 1 (first-order) | $\beta = S'$ has a positive local minimum ($\beta$ backbends; latent heat) |
| 2 (second-order) | $\gamma = S''$ has a negative local maximum |
| 3 | $\delta = S'''$ has a positive local minimum |
| odd $n$ | positive local minimum of $S^{(n)}$ |
| even $n$ | negative local maximum of $S^{(n)}$ |
Dependent transitions carry the swapped signatures (odd $n$: negative local maximum; even $n$: positive local minimum) and are reported with kind = :dependent when accompanied by an independent transition of lower order at lower energy. The transition temperature is $T_\mathrm{tr} = 1/(k_B\,\beta(E_\mathrm{tr}))$ — in Kelvin with the default kb in eV/K, or in energy units with kb = 1. This is a purely microcanonical route: it needs no temperature grid, returns transition energies and orders directly, and can resolve transitions that broad canonical cv(T) peaks blur.
Workflow
using FreeBird
df = read_output("ladder.csv") # canonical NS output (columns :iter, :emax)
K = 320 # number of live walkers used to produce it
E, S = microcanonical_entropy(df, K) # S(E) = ln g(E)
d = caloric_derivatives(df, K; max_order = 2) # d.E, d.β, d.γ
ts = inflection_transitions(df, K; max_order = 2) # → (E_tr, T_tr, order, kind, strength)
# Recommended: confirm each transition is converged in the walker count K
dfs = [read_output("ladder_K320.csv"),
read_output("ladder_K640.csv"),
read_output("ladder_K1280.csv")]
conv = transition_convergence(dfs, [320, 640, 1280]) # flags drifting transitionsConvergence and smoothing
The derivative $\gamma = d^2S/dE^2$ amplifies the finite-walker "staircase" granularity of the NS ladder (step $\sim 1/(K\,g)$). This roughness is deterministic in the walker count $K$ — it does not average out over independent runs — so a transition should only be trusted once its temperature and order stop drifting as $K$ grows; use transition_convergence. Near-ground transitions converge last. The light internal smoothing (halfwidth) is a necessary differentiation aid, not a substitute for adequate $K$: smoothing heavily to mask too-few walkers biases the transition temperatures. Energy-window/ground_trim controls remove the $\beta$ divergence as $E \to E_\mathrm{ground}$.
Functions
FreeBird.AnalysisTools — Module
AnalysisToolsModule for analyzing the output of the sampling.
FreeBird.AnalysisTools._fixed_N_log_evidence — Method
_fixed_N_log_evidence(df, T_grid; n_walkers, n_cull, ω0, live_energies, kb,
observable_cols=Symbol[], live_observables=nothing)
-> (log_Z_NS::Vector{Float64}, mean_E::Vector{Float64},
mean_E2::Vector{Float64}, mean_obs::Dict{Symbol,Vector{Float64}})Canonical NS evidence log Z_NS(β) = log Σᵢ ωᵢ exp(−βEᵢ) and canonical mean energy at each temperature in T_grid, evaluated by log-sum-exp. The discarded weights ωᵢ are built directly in log space, so deep ladders (iter beyond ~700 · n_walkers, where the linear-space ωᵢ underflows Float64) keep their low-energy samples. When live_energies is supplied, the residual prior mass ω0 · (K/(K+n_cull))^{n_iters} (with n_iters = maximum(df.iter)) is split evenly among the supplied energies — the live-set tail correction. An empty ladder records no compression, so its tail carries the sector's entire prior mass, exactly 1 and independent of ω0 (matching the internally-handled N = 0 sector). mean_E2 is the canonical second moment of the shifted energies, ⟨(E − energy_shift)²⟩(β) — the caller supplies one global energy_shift so sector moments combine consistently and the one-pass variance avoids the ~eps·E² cancellation floor (mean_E stays absolute) — and mean_obs carries the canonical average of each requested per-dead-point observable column (dead values from df[!, col], live-tail values from live_observables[col], aligned with live_energies). Internal helper shared by the gc_thermodynamic_stats_fixed_N methods; the caller validates the observable inputs.
FreeBird.AnalysisTools._log_binomial — Method
_log_binomial(M::Integer, N::Integer) -> Float64Return log(binomial(M, N)) in floating point, computed from _log_factorial so that it does not overflow for lattice sizes where binomial(M, N) exceeds typemax(Int64).
FreeBird.AnalysisTools._log_factorial — Method
_log_factorial(n::Integer) -> Float64Return log(n!) for non-negative n, computed by summing log(k). Accurate to roundoff accumulation (absolute error ~1e-11 at n ~ 10^4), which covers both the small-n atomistic use and the large-M _log_binomial use.
FreeBird.AnalysisTools._log_poisson_partial_sum — Method
_log_poisson_partial_sum(z0V::Float64, n_max::Int)The log reference mass of the truncated continuous ideal-gas measure: log(Σ_{N=0}^{n_max} (z0V)^N / N!), computed in log space with a max-shifted logsumexp so neither (z0V)^N nor N! overflows. Exactly 0.0 at n_max = 0 (the empty-configuration term alone) and tends to z0V as n_max grows (the unbounded mass e^{z0V}).
FreeBird.AnalysisTools._thermal_wavelength — Method
_thermal_wavelength(atomic_mass::typeof(1.0u"u"), T::Unitful.Temperature) -> typeof(1.0u"Å")Compute the thermal de Broglie wavelength Λ = h / sqrt(2π m k_B T) in Å. Internal helper for gc_thermodynamic_stats_fixed_N.
FreeBird.AnalysisTools.caloric_derivatives — Method
caloric_derivatives(df::DataFrame, n_walkers::Int;
n_cull=1, max_order=2, n_nodes=400, halfwidth=0)Microcanonical entropy derivatives from a nested-sampling output df: $β(E)=dS/dE$ (inverse caloric temperature; the microcanonical temperature is $k_BT=1/β$), $γ(E)=d^2S/dE^2$, and optionally $δ(E)=d^3S/dE^3$.
Derivatives are computed in iteration-index space (see microcanonical_entropy) and then differentiated against energy with local cubic fits.
Arguments
max_order: highest derivative to return (1 → β only; 2 → β, γ; 3 → β, γ, δ).n_cull,n_nodes,halfwidth: as inmicrocanonical_entropy.
Returns
A NamedTuple with E (ascending energies, same units as :emax) and β (units of inverse energy), plus γ and δ as requested by max_order. Note γ, δ are sensitive to NS statistics — increase the walker count K (not the number of seeds) for cleaner higher derivatives; see transition_convergence. They are also computed over the full energy range, including the near-ground region where the ladder flattens and the β estimate diverges — inflection_transitions trims that region (ground_trim) before differentiating; do the same before interpreting low-energy γ/δ features.
FreeBird.AnalysisTools.cv — Method
cv(df::DataFrame, βs::Vector{Float64}, dof::Int, n_walkers::Int)(Nested Sampling) Calculates the constant-volume heat capacity at constant volume for the given DataFrame, inverse temperatures, degrees of freedom, and number of walkers. The heat capacity is defined as:
\[C_V(\beta) = \frac{\mathrm{dof} \cdot k_B}{2} + k_B \beta^2 \left(\frac{\sum_i \omega_i E_i^2 \exp(-E_i \beta)}{Z(\beta)} - U(\beta)^2\right)\]
where $\mathrm{dof}$ is the degrees of freedom, $k_B$ is the Boltzmann constant (in units of eV/K), $\beta$ is the inverse temperature, $\omega_i$ is the $i$-th $\omega$ factor, $E_i$ is the $i$-th energy, $Z(\beta)$ is the partition function, and $U(\beta)$ is the internal energy.
Arguments
df::DataFrame: The DataFrame containing the output data.βs::Vector{Float64}: Inverse temperatures.dof::Int: The degrees of freedom, equal to the number of dimensions times the number of particles. For a lattice, it is zero.n_walkers::Int: The number of walkers.n_cull::Int: The number of culled walkers. Default is 1.ω0::Float64: The initial $\omega$ factor. Default is 1.0.
Returns
- A vector of constant-volume heat capacities.
FreeBird.AnalysisTools.cv — Method
cv(β::Float64, omega_i::Vector{Float64}, Ei::Vector{Float64}, dof::Int)Calculates the constant-volume heat capacity for the given $\beta$, $\omega$ factors, energies, and degrees of freedom. The heat capacity is defined as:
\[C_V(\beta) = \frac{\mathrm{dof} \cdot k_B}{2} + k_B \beta^2 \left(\frac{\sum_i \omega_i E_i^2 \exp(-E_i \beta)}{Z(\beta)} - U(\beta)^2\right)\]
where $\mathrm{dof}$ is the degrees of freedom, $k_B$ is the Boltzmann constant (in units of eV/K), $\beta$ is the inverse temperature, $\omega_i$ is the $i$-th $\omega$ factor, $E_i$ is the $i$-th energy, $Z(\beta)$ is the partition function, and $U(\beta)$ is the internal energy.
Arguments
β::Float64: The inverse temperature.ωi::Vector{Float64}: The $\omega$ factors.Ei::Vector{Float64}: The energies in eV.dof::Int: The degrees of freedom, equals to the number of dimensions times the number of particles.
Returns
- The constant-volume heat capacity.
FreeBird.AnalysisTools.cv — Method
cv(Ts::Vector{Float64}, dof::Int, energy_bins::Vector{Float64}, entropy::Vector{Float64})(Wang-Landau Sampling) Calculates the constant-volume heat capacity at constant volume for the given temperatures, degrees of freedom, energy bins, and entropy. The kinetic energy is treated classically, and is added to the heat capacity as $dof \cdot k_B/2$.
Arguments
Ts::Vector{Float64}: The temperatures in Kelvin.dof::Int: The degrees of freedom, equals to the number of dimensions times the number of particles. For a lattice, it is zero.energy_bins::Vector{Float64}: The energy bins in eV.entropy::Vector{Float64}: The entropy.
Returns
- A vector of constant-volume heat capacities.
FreeBird.AnalysisTools.gc_effective_sample_size_ideal_ref — Method
gc_effective_sample_size_ideal_ref(df::DataFrame, n_sites::Int, z0::Float64,
μs::Vector{Float64}, Ts::Vector{Float64},
n_walkers::Int; kwargs...)
gc_effective_sample_size_ideal_ref(df::DataFrame, V, atomic_mass,
reference_activity, μ_grid, T_grid; kwargs...)Effective-sample-size reduction for ideal-gas-referenced grand-canonical nested-sampling output: returns a Matrix{Float64} of size (length(μs), length(Ts)), indexed [i_μ, i_T] like the fields of gc_thermodynamic_stats_ideal_ref, without evaluating any of that function's moment or distribution surfaces. The first method is the lattice route (z = exp(βμ), shell weights rebuilt from df.iter); the second is the atomistic route (z = exp(βμ)/Λ(T)^3, shell weights decoded from df.log_compression), mirroring the sibling's dispatch, ledger conventions, and live-tail handling exactly. n_cull and n_walkers are lattice-route arguments only.
Three modes, selected by the mode keyword:
:kish(default): the Kish effective sample size of the full combined weightsω_j (z/z0)^{N_j} e^{-βE_j}— identical in value to theN_efffield ofgc_thermodynamic_stats_ideal_ref.:anchored: the Kish effective sample size with the reweighting factor anchored to a reference temperature,r_j = (z/z0)^{N_j} exp[-(E_j/k_B)(1/T - 1/T0)]. The defaultT0 = nothingmeans per-targetT0 = T, under which the energy factor drops out entirely and the mode measures μ-reweighting concentration alone; at the reference point it then equals the run-independent prior-weight value(1 + r)(1 - r^J)^2 / ((1 - r)(1 - r^(2J)))withr = K/(K + n_cull)(about2K + 1on deep ladders atn_cull = 1;2K/n_cull + 1in general), which is what makes it thresholdable.:anchored_uniform: the unit-weight effective sample size of the anchored factors alone,(Σ r_j)^2 / Σ r_j^2. Under the default per-targetT0it peaks at the reference point (where it equals the row count exactly) and decays monotonically along rays away from it, unlike the two Kish modes, so it locates a window center.
relative = true divides each temperature column by the same mode's value at that temperature's reference point — the chemical potential μ0(T) = k_B T ln z0 (lattice) or k_B T (ln z0 + 3 ln Λ(T)) (atomistic) where the reweighting exponent vanishes; the anchor is evaluated with the exponent set to zero exactly, not through a rounded μ0. The returned surface then reads directly as degradation relative to the run's own center. The anchor value of :kish is not a maximum and the surface need not decrease monotonically away from it: a target whose thermal factor counteracts the geometric shell decay flattens the combined weights and raises the value.
The effective sample size diagnoses μ-reweighting reliability, not ladder convergence: pooled live-tail rows can hold it near K on an under-converged ladder. Passing T0 with mode = :kish throws an ArgumentError.
Arguments (lattice method)
df::DataFrame: NS output with columns[:iter, :emax, :num_particles].n_sites::Int: Number of lattice sites M.z0::Float64: Reference fugacity of the prior used in the run (must match!).μs::Vector{Float64}: Chemical potential grid (same energy units asdf.emax).Ts::Vector{Float64}: Temperature grid in K.n_walkers::Int: Number of walkers K used in the NS run.mode::Symbol=:kish::kish,:anchored, or:anchored_uniform(above).relative::Bool=false: Divide each column by its reference-point value.T0::Union{Nothing,Float64}=nothing: Anchor temperature in K for the anchored modes;nothingmeans per-targetT0 = T.n_cull::Int=1: Number of walkers culled per iteration.ω0::Float64=1.0: Initial phase-space volume factor.live_emax::Union{Nothing,Vector{Float64}}=nothing: Energies of the surviving live walkers.live_numbers::Union{Nothing,Vector{Int}}=nothing: Particle counts of the surviving live walkers.kb::Float64: Boltzmann constant (default: eV/K).
The atomistic method replaces (n_sites, z0, n_walkers) with (V, atomic_mass, reference_activity) in the sibling's Unitful types, takes Unitful μ_grid/T_grid, types T0 as a Unitful temperature, and requires the ledger's :log_compression column whenever df has rows.
Returns
- A
Matrix{Float64}of size(length(μs), length(Ts)), indexed[i_μ, i_T].
FreeBird.AnalysisTools.gc_thermodynamic_stats — Method
gc_thermodynamic_stats(df::DataFrame, βs::Vector{Float64},
n_walkers::Int, μ::Float64;
n_cull::Int=1, ω0::Float64=1.0,
kb::Float64=8.617333262e-5)Compute grand-canonical thermodynamic stats from a GC-NS output DataFrame.
The DataFrame must have columns :iter, :omega, :energy, :num_particles. Adds the live-walker contribution to the end of the recorded samples for correct normalization.
Arguments
df::DataFrame: GC-NS output with columns[:iter, :omega, :energy, :num_particles].βs::Vector{Float64}: Inverse temperatures at which to evaluate.n_walkers::Int: Number of walkers used in the NS run.μ::Float64: Chemical potential.n_cull::Int=1: Number of walkers culled per iteration.ω0::Float64=1.0: Initial phase-space volume.kb::Float64: Boltzmann constant (default: eV/K).
Returns
(mean_E, Cv, mean_N): Vectors of ⟨E⟩, C_{V,μ}, and ⟨N⟩ at each β.
FreeBird.AnalysisTools.gc_thermodynamic_stats — Method
gc_thermodynamic_stats(β::Float64, ωi::Vector{Float64},
grand_energies::Vector{Float64},
energies::Vector{Float64},
numbers::Vector{Int},
μ::Float64;
kb::Float64=8.617333262e-5)Compute grand-canonical thermodynamic averages from nested sampling output.
The log-sum-exp trick is used for numerical stability. The grand-canonical heat capacity at constant μ is:
C_{V,μ} = k_B β² [Var(E) − μ Cov(E, N)]Arguments
β::Float64: Inverse temperature 1/(k_B T).ωi::Vector{Float64}: Phase-space volume weights from NS.grand_energies::Vector{Float64}: Ωi = Ei − μ N_i values.energies::Vector{Float64}: E_i values.numbers::Vector{Int}: N_i values.μ::Float64: Chemical potential.kb::Float64: Boltzmann constant (default: eV/K).
Returns
(⟨E⟩, C_{V,μ}, ⟨N⟩): Mean energy, GC heat capacity, mean particle number.
FreeBird.AnalysisTools.gc_thermodynamic_stats_fixed_N — Method
gc_thermodynamic_stats_fixed_N(ns_outputs, N_values, n_sites, μ_grid, T_grid;
n_walkers=120, n_cull=1, ω0=1.0,
live_emax=nothing, kb=8.617333262e-5)Lattice-gas method: compute grand-canonical thermodynamic averages from a stack of canonical (fixed-N) lattice nested-sampling outputs, one per particle count N, on a lattice with n_sites sites.
Fixed-N lattice NS samples the uniform prior over the binomial(n_sites, N) occupation patterns, so the absolute canonical partition function is Z_N(β) = binomial(M, N) · Z_NS^{(N)}(β) with M = n_sites and
\[\Xi(\mu, T) = \sum_N z^N \binom{M}{N} Z_{\mathrm{NS}}^{(N)}(\beta), \qquad z = \exp(\beta\mu).\]
A lattice gas has no momentum integral, so z = exp(βμ) directly — no volume argument and no thermal wavelength enter (contrast the atomistic method of this function). The sum runs over the supplied N_values, which must include 0; truncating N_values below n_sites truncates Ξ accordingly, which is safe only when ⟨N⟩ at the requested (μ, T) sits well below the largest supplied N. (For an athermal hard-core model the truncation at the close-packing N is exact — every higher sector has no allowed configuration; see the hard-core recipe in the GenericLatticeHamiltonian docstring for the per-N sampling setup and the evaluation-temperature criterion.)
Special sectors
N = 0: the empty lattice is a single configuration withE = 0, soZ_NS^{(0)} = 1; the corresponding DataFrame contents are ignored.N = n_sites(and any other single-configuration sector): NS cannot make progress because every walker holds the same configuration. Supply an empty DataFrame (DataFrame(iter=Int[], emax=Float64[])) together with alive_emaxentry holding the sector energy (conventionallyn_walkerscopies of it; any count works) — the live-set tail then carries the sector's entire prior mass, exactly1and independent ofω0, matching the internally-handledN = 0sector. Because such sectors require alive_emaxentry, covering the full range0:n_sitesinN_valuesis only possible withlive_emaxsupplied.
Live-set tail and normalization
As for the atomistic method, the recorded weights ωᵢ carry only part of the prior volume after finite NS termination. Supplying live_emax (one vector of live-walker energies per N, normally the n_walkers live energies) splits the residual prior mass ω0 · (K/(K+n_cull))^{n_iters} evenly among the supplied entries of each sector. For fully-normalized absolute Ξ, pass ω0 = (n_walkers + n_cull)/n_walkers (Skilling weights) together with live_emax; with the defaults (ω0 = 1.0, no tail), logXi is biased low, and the omitted tail additionally leaves an N-dependent bias — small but visible at low T or for shallow NS runs, and not exactly cancelling in the ratio observables mean_N, var_N, and mean_U when per-N ladders differ in length or convergence.
Arguments
ns_outputs::AbstractVector{<:DataFrame}: one canonical lattice-NS output perN, each with columns[:iter, :emax](energies in eV, as produced bynested_samplingon aLatticeGasWalkersliveset).N_values::AbstractVector{<:Integer}: particle counts corresponding to each DataFrame. Must include0, and every entry must lie in0:n_sites.n_sites::Integer: number of lattice sitesM.μ_grid::AbstractVector{<:typeof(1.0u"eV")}: chemical potentials.T_grid::AbstractVector{<:Unitful.Temperature}: temperatures.
Keyword arguments
n_walkers::Int=120: number of NS walkers used to produce each DataFrame. Must be uniform acrossns_outputs.n_cull::Int=1: NS culls per iteration.ω0::Float64=1.0: initial prior weight for the discarded-sample ladder.live_emax::Union{Nothing,AbstractVector{<:AbstractVector{<:Real}}}=nothing: when supplied, one vector of live-walker energies (in eV) perN— normally then_walkerslive energies; the sector's residual prior mass is split evenly among the supplied entries. The entry forN=0is ignored.observable_cols::AbstractVector{Symbol}=Symbol[]: names of extra ledger columns (recorded per dead point in each sector's canonical run, e.g. via theobservableskeyword ofnested_sampling) whose grand-canonical averages ⟨A⟩(μ, T) are returned inobservables. Requesting observables requireslive_emaxandlive_observables.live_observables=nothing: oneDict{Symbol,<:AbstractVector{<:Real}}perNsector (aligned withN_values), holding each observable's values on that sector's surviving live walkers (same order and length as the sector'slive_emaxentry). TheN = 0entry is used, unlike its ignoredlive_emaxcounterpart: it supplies the observable value of the single empty-lattice configuration (e.g.[0.0]for a sublattice order parameter). Single-configuration sectors follow the empty-DataFrame convention, with their observable value in the live entry.kb::Float64: Boltzmann constant in eV/K.
Returns
A NamedTuple (logXi, mean_N, var_N, mean_U, log_Z_N, N_values, var_U, cov_UN, p_N, N_support, observables); p_N (Array{Float64,3} indexed [i_μ, i_T, i_N]) is the particle-number distribution over N_support::Vector{Int}, the sorted sector list. The first four fields are Matrix{Float64} of size (length(μ_grid), length(T_grid)) indexed [i_μ, i_T]. logXi is the natural log of the absolute grand partition function — returned in log space (the atomistic method returns linear-space Xi alongside the same logXi) because the binomial prior mass grows like 2^M and Ξ overflows Float64 for modest lattices. mean_N is ⟨N⟩, var_N is ⟨N²⟩ − ⟨N⟩², and mean_U is ⟨E⟩ (grand-canonical, in eV). log_Z_N is the (length(N_values), length(T_grid)) matrix of log canonical partition-function slices log[C(M,N) · Z_NS^{(N)}(β)] — the per-N evidence the assembly is built from — and N_values echoes the particle counts (as Vector{Int}) indexing its rows. For an athermal (hard-core) model evaluated at a sufficiently low T (see the hard-core recipe in the GenericLatticeHamiltonian docstring), log_Z_N is log g_N, the log count of allowed N-particle configurations. var_U is the grand-canonical energy variance ⟨E²⟩ − ⟨E⟩² and cov_UN the covariance ⟨EN⟩ − ⟨E⟩⟨N⟩, both assembled by the law of total expectation over the N sectors (the grand-canonical heat capacity follows as C = k_B β² (var_U − μ · cov_UN)). observables is a Dict{Symbol,Matrix{Float64}} mapping each requested column to ⟨A⟩(μ, T); empty when no columns are requested.
FreeBird.AnalysisTools.gc_thermodynamic_stats_fixed_N — Method
gc_thermodynamic_stats_fixed_N(ns_outputs, N_values, V, atomic_mass, μ_grid, T_grid;
n_walkers=120, n_cull=1, ω0=1.0,
live_emax=nothing,
observable_cols=Symbol[], live_observables=nothing,
empty_energy=0.0, kb=8.617333262e-5)Atomistic method: compute grand-canonical thermodynamic averages from a stack of canonical nested-sampling outputs, one per fixed particle number N, using the simulation-box volume V and the thermal wavelength. Returns the same field set as the lattice-gas method of this function (which takes n_sites), with the linear-space Xi retained in the leading position alongside its log-space companion logXi.
For each N, the canonical NS evidence at inverse temperature β is
\[Z_{\mathrm{NS}}^{(N)}(\beta) = \sum_i \omega_i \exp(-\beta E_i)\]
— the prior-volume-normalized configurational integral. The absolute configurational partition function is Z_N^{config}(\beta) = V^N \cdot Z_{NS}^{(N)}(\beta). The grand partition function is assembled as
\[\Xi(\mu, T) = \sum_N \frac{(zV)^N}{N!}\, Z_{\mathrm{NS}}^{(N)}(\beta), \qquad z = \frac{\exp(\beta\mu)}{\Lambda(T)^3}\]
with the thermal wavelength Λ(T) = h / sqrt(2π m k_B T) computed from atomic_mass. The sum runs over the supplied N_values, which must include 0. The N=0 sector is treated specially: the empty configuration has no spatial integral, so Z_{NS}^{(0)} = exp(-β·empty_energy) — exactly 1 at the default empty_energy = 0 — and the corresponding DataFrame contents are ignored. Truncation error at the upper end of N_values is bounded by the tail of (zV)^N / N! for the largest ⟨N⟩ requested.
All sectors must share one zero of energy. When the recorded energies carry a configuration-independent offset — for surface systems, the frozen substrate's self-energy, which every N ≥ 1 walker records via energy_frozen_part — pass that offset as empty_energy so the N = 0 sector sits on the same scale. Otherwise the empty sector's weight in the grand sum is misstated by the factor exp(+β·offset), and for an attractive substrate the assembly silently returns N ≥ 1-conditional statistics wherever the empty sector carries weight (dilute coverage, strong binding, low temperature).
A log-sum-exp pass is used both inside each per-N evidence and across the grand sum for numerical stability.
Live-set tail correction
After a finite number of NS iterations n_iters the recorded weights ωᵢ carry only 1 − (K/(K+n_cull))^{n_iters} of the prior volume (times ω0); the remainder sits in the K surviving live walkers. Supplying live_emax (one vector of live walker energies per N, normally the K live energies) adds the live-set tail to each per-N evidence: the residual mass ω0 · (K/(K+n_cull))^{n_iters} is split evenly among the supplied energies. A sector supplied as an empty DataFrame plus live energies carries mass exactly 1, independent of ω0 (see the lattice-gas method's "Special sectors"). When omitted, the live-set tail is neglected — for ratio observables (⟨N⟩, ⟨U⟩) the resulting bias is small but visible at low T or shallow NS; for the absolute Ξ it appears as a uniform-in-N prefactor that does not cancel.
Arguments
ns_outputs::AbstractVector{<:DataFrame}: one canonical-NS output perN, each with columns[:iter, :emax](matching the schema produced bynested_sampling). The entry corresponding toN=0is ignored and may be any DataFrame (e.g.,DataFrame(iter=Int[], emax=Float64[])).N_values::AbstractVector{<:Integer}: particle counts corresponding to each DataFrame. Must include0;length(N_values) == length(ns_outputs).V::typeof(1.0u"Å^3"): the simulation-box volume, i.e. the NS prior volume per particle (NS samples positions uniformly over the box). This is what closesZ_N^{config} = V^N · Z_{NS}^{(N)}, so it must not be reduced to an accessible or adsorption-region sub-volume. For surface systems the substrate's volume exclusion is captured by the Boltzmann factor insideZ_{NS}^{(N)}, not by shrinkingV.atomic_mass::typeof(1.0u"u"): per-atom mass forΛ(T).μ_grid::AbstractVector{<:typeof(1.0u"eV")}: chemical potentials.T_grid::AbstractVector{<:Unitful.Temperature}: temperatures.
Keyword arguments
n_walkers::Int=120: number of NS walkers used to produce each DataFrame. Must be uniform acrossns_outputs.n_cull::Int=1: NS culls per iteration.ω0::Float64=1.0: initial prior weight, passed toωᵢ.live_emax::Union{Nothing,AbstractVector{<:AbstractVector{<:Real}}}=nothing: when supplied, one vector ofK = n_walkerslive walker energies (in eV) perN. The entry forN=0is ignored. See "Live-set tail correction" above.observable_cols::AbstractVector{Symbol}=Symbol[]: names of extra ledger columns to average grand-canonically; eachN ≥ 1DataFrame must carry them. Requireslive_emaxandlive_observables.live_observables=nothing: oneDict{Symbol,<:AbstractVector{<:Real}}perNsector, holding each observable's values on the surviving live walkers. TheN = 0entry supplies the observable value of the empty configuration directly (averaged over the given values), unlike its ignoredlive_emaxentry.empty_energy::Float64=0.0: total energy (in eV) of theN=0configuration on the same energy scale as the ledger energies. Leave at0when the empty simulation box has zero energy (free clusters and fluids); for adsorption on a frozen substrate pass the substrate self-energy. Must be finite. At very large offsets (β·|empty_energy|beyond about709) the linear-spaceXireturn over- or underflows while the ratio observables (mean_N,var_N,mean_U) remain valid — as doeslogXi; when the absoluteΞis needed in that regime, readlogXi.kb::Float64: Boltzmann constant in eV/K.
Returns
A NamedTuple (Xi, mean_N, var_N, mean_U, logXi, var_U, cov_UN, log_Z_N, N_values, p_N, N_support, observables); the first four fields keep their historical leading positions. Xi, mean_N, var_N, mean_U, logXi, var_U, and cov_UN are Matrix{Float64} of size (length(μ_grid), length(T_grid)) indexed [i_μ, i_T]. Xi is the absolute grand partition function in linear space — Inf once ln Ξ exceeds ≈ 709; logXi, its log-space companion, stays finite there. mean_N is ⟨N⟩, var_N is ⟨N²⟩ − ⟨N⟩², and mean_U is ⟨E⟩ (grand-canonical, in eV). var_U is the grand-canonical energy variance ⟨E²⟩ − ⟨E⟩² and cov_UN the covariance ⟨EN⟩ − ⟨E⟩⟨N⟩, both assembled by the law of total expectation over the N sectors. At fixed μ the configurational heat capacity follows as C_config = k_B β² (var_U − μ · cov_UN) + (3/2) k_B β · cov_UN: the extra term carries the Λ(T)^{3N} temperature dependence of the atomistic activity and is absent from the lattice-gas method's identity; kinetic contributions must be added separately for the total-energy heat capacity. Both fields are accurate to roughly machine epsilon times their natural scales, which near their zero crossings is absolute rather than relative precision. log_Z_N is the (length(N_values), length(T_grid)) matrix of per-N log canonical partition functions log Z_N(T) = log Z_NS^{(N)}(β) + N·log(V/Λ³) − log N! — the μ-independent evidence the assembly is built from — and N_values echoes the particle counts (as Vector{Int}) indexing its rows; the particle-number distribution follows as P(N | μ, T) ∝ exp.(log_Z_N[:, j] .+ β .* μ .* N_values), normalized over the supplied sectors, and is returned ready-made as p_N (Array{Float64,3} indexed [i_μ, i_T, i_N]) over N_support::Vector{Int}, the sorted sector list (a vector, not a range: the fixed-N routes accept non-contiguous sectors). observables is a Dict{Symbol,Matrix{Float64}} mapping each requested column to ⟨A⟩(μ, T); empty when no columns are requested. No Kish effective sample size is returned: the fixed-N route introduces no importance reweighting (μ enters the assembly exactly), so there is no sampling fidelity for it to diagnose.
FreeBird.AnalysisTools.gc_thermodynamic_stats_ideal_ref — Method
gc_thermodynamic_stats_ideal_ref(df::DataFrame, n_sites::Int, z0::Float64,
μs::Vector{Float64}, Ts::Vector{Float64},
n_walkers::Int;
n_cull::Int=1, ω0::Float64=1.0,
live_emax::Union{Nothing,Vector{Float64}}=nothing,
live_numbers::Union{Nothing,Vector{Int}}=nothing,
kb::Float64=8.617333262e-5)Assemble grand-canonical thermodynamics on a (μ, T) grid from a single ideal-gas-referenced nested sampling run (ideal_gas_referenced_nested_sampling in SamplingSchemes).
The run samples the ideal-lattice-gas prior at reference fugacity z0 (configuration weight z0^N, total prior mass (1 + z0)^M on M = n_sites sites) and culls by energy alone. The absolute grand partition function at any target chemical potential μ and temperature T is then
\[\Xi(\mu, T) = (1 + z_0)^M \sum_j \omega_j \left(\frac{z}{z_0}\right)^{N_j} e^{-\beta E_j}, \qquad z = e^{\beta\mu},\]
where the sum runs over culled walkers (plus, when live_emax/live_numbers are given, the surviving live walkers, each with residual weight (K/(K+n_cull))^{n_iters} / K — no ω0 factor, since ω0 corrects the dead-sample shell weights only). There is no thermal-wavelength factor: a lattice gas has no momentum degrees of freedom, so z = exp(βμ) directly. All sums are evaluated with the log-sum-exp trick, and Ξ is returned as log Ξ to avoid overflow at low temperature.
For a fully normalized absolute Ξ, pass ω0 = (n_walkers + n_cull)/n_walkers (Skilling weights) together with the live-walker tail — the dead weights then sum to 1 − (K/(K+n_cull))^{n_iters} and the tail supplies the remainder, so Σω = 1 exactly. The default ω0 = 1.0 underestimates Ξ by a factor K/(K+n_cull) and neglects the tail (see the ωᵢ conventions). Ratio observables (mean_N, var_N, mean_U) are insensitive to ω0.
The reweighting factor (z/z0)^{N_j} is pure importance sampling in μ: its reliability at each grid point is reported by the Kish effective sample size N_eff = (Σ w)² / Σ w², which collapses as |βμ − ln z0| grows beyond roughly 1/√Var(N). Because the combined weights also carry the shell decay and the Boltzmann factor, the raw N_eff has no run-independent baseline — judge grid points by degradation relative to the run's own reference point μ0(T) = k_B T ln z0 (a threshold on the ratio, computed by gc_effective_sample_size_ideal_ref with relative = true) rather than by an absolute cutoff, and re-run with z0 closer to the target fugacity when the ratio collapses. N_eff diagnoses μ-reweighting reliability, not ladder convergence: pooled live-tail rows can hold it near K on an under-converged ladder.
Athermal (hard-core) models sampled with the finite-J recipe (see the GenericLatticeHamiltonian docstring) are evaluated here at a temperature low enough that β·J exceeds the ladder depth in nats by ~40 while β·δ ≪ 1 (δ = the run's energy_perturbation): the violating shells' e^{-βE_j} are then exact zeros, the retained shells' deviate from 1 only by O(β·δ), and every observable is a function of z = exp(βμ) alone (mean_U reports the residual tie-breaking noise, ~δ).
Arguments
df::DataFrame: NS output with columns[:iter, :emax, :num_particles].n_sites::Int: Number of lattice sites M.z0::Float64: Reference fugacity of the prior used in the run (must match!).μs::Vector{Float64}: Chemical potential grid (same energy units asdf.emax, e.g. eV).Ts::Vector{Float64}: Temperature grid in K.n_walkers::Int: Number of walkers K used in the NS run.n_cull::Int=1: Number of walkers culled per iteration.ω0::Float64=1.0: Initial phase-space volume factor.live_emax::Union{Nothing,Vector{Float64}}=nothing: Energies of the surviving live walkers.live_numbers::Union{Nothing,Vector{Int}}=nothing: Particle counts of the surviving live walkers.observable_cols::AbstractVector{Symbol}=Symbol[]: Names of extradfcolumns (recorded per dead point, e.g. via theobservableskeyword ofideal_gas_referenced_nested_sampling) whose reweighted averages ⟨A⟩(μ, T) are returned inobservables. Requesting:num_particlesor:emaxlegitimately reproducesmean_N/mean_U(a self-consistency check).live_observables=nothing: Required wheneverobservable_colsis non-empty and a live-set tail is supplied — aDict{Symbol,<:AbstractVector{<:Real}}with exactly theobservable_colskeys, holding each observable's values on the surviving live walkers (one entry perlive_emaxentry, same order). Omitting the tail values would silently deflate every ⟨A⟩, so this is an error rather than a default.kb::Float64: Boltzmann constant (default: eV/K).
Returns
A NamedTuple; every field except observables is a Matrix{Float64} of size (length(μs), length(Ts)), indexed [i_μ, i_T]:
logXi: Natural log of the absolute grand partition function.mean_N: Mean particle number ⟨N⟩.var_N: Particle-number variance ⟨N²⟩ − ⟨N⟩².mean_U: Mean configurational energy ⟨E⟩.N_eff: Kish effective sample size of the reweighted estimate.var_U: Energy variance ⟨E²⟩ − ⟨E⟩². The grand-canonical heat capacity follows asC = k_B β² (var_U − μ · cov_UN), matching the Ω-sortedgc_thermodynamic_statsformula.cov_UN: Energy–particle-number covariance ⟨EN⟩ − ⟨E⟩⟨N⟩.p_N:Array{Float64,3}indexed[i_μ, i_T, i_N], the particle-number distribution P(N | μ, T) overN_support; a support member no sample visited carries exactly 0.0 (a convergence diagnostic, not physics).N_support:UnitRange{Int}0:N_maxover the union of dead rows and the live tail, indexing the third axis ofp_N.observables:Dict{Symbol,Matrix{Float64}}mapping each requested column to its reweighted average ⟨A⟩(μ, T); empty when no columns are requested.
FreeBird.AnalysisTools.gc_thermodynamic_stats_ideal_ref — Method
gc_thermodynamic_stats_ideal_ref(df::DataFrame,
V::typeof(1.0u"Å^3"),
atomic_mass::typeof(1.0u"u"),
reference_activity::typeof(1.0u"Å^-3"),
μ_grid::AbstractVector{<:typeof(1.0u"eV")},
T_grid::AbstractVector{<:Unitful.Temperature};
ω0=1.0, live_emax=nothing, live_numbers=nothing,
observable_cols=Symbol[], live_observables=nothing,
kb=8.617333262e-5, n_max=nothing)Reduce an atomistic energy-sorted ideal-gas-referenced grand-canonical ledger (see the AtomisticIGRefGCNSParameters method of ideal_gas_referenced_nested_sampling) to Ξ(μ, T) on an arbitrary (μ, T) grid. The assembly is
\[\log \Xi(\mu, T) = z_0 V + \mathrm{logsumexp}_j\left[\log \omega_j + N_j \log(z/z_0) - E_j/k_B T\right]\]
with z = exp(μ/kT)/Λ(T)³ the target activity, z0 the run's reference activity, and e^{z0V} the reference measure's total mass. The thermal wavelength and the volume enter only here: the sampler is athermal and its ledger is reduced, never re-run.
For a BOUNDED run (a finite n_max on AtomisticIGRefGCNSParameters), pass the same n_max here: the reference measure is then the truncated ideal gas on 0:n_max, whose total mass Σ_{N=0}^{n_max} (z0V)^N/N! replaces e^{z0V} in the assembly (_log_poisson_partial_sum). The reweighting exponent, every ratio observable, p_N, and N_eff are unchanged — the reference mass is a global constant that cancels from all of them — so a bounded run reduced with the default n_max=nothing differs ONLY in logXi, shifted up by exactly -log P(Poisson(z0V) <= n_max) (the surplus reference mass above the cap). Ledger or live-tail entries with N > n_max are rejected: they cannot have come from the claimed bounded run.
Three conventions are fixed by measurement and documented here:
- Shell weights follow the log-compression convention of
ωᵢ(log_compression; ω0=1.0), evaluated in log space (log ω_j = log X_{j-1} + log(1 - t_j)) so deep ladders cannot underflow (the same rationale as the iteration-based assembly above). Theω0 = (K+1)/Kconvention of the iteration-based route must not be applied to compression ledgers: it inflates the total prior mass by exactly1/K. - The live-set tail carries the mass
ω0·exp(Σ log_compression)split overlength(live_emax), never over the nominal walker count: a run that ends inside a plateau eviction block has fewer live walkers than it started with, and splitting over the nominal count loses(1 - live/K)of the terminal mass. - A ledger with zero rows is a legal input, produced by zero-accept runs (see the driver's stall contract): the entire prior mass then sits in the live set, which is the exact reduction for a fully degenerate landscape. A ledger that has rows but no
:log_compressioncolumn is rejected: its compression cannot be reconstructed here.
Convention differences among the grand-canonical stats functions: this method and the lattice gc_thermodynamic_stats_ideal_ref reduce one variable-N ledger against their reference measures (e^{z0V} here, (1+z0)^M there); gc_thermodynamic_stats_fixed_N stitches per-N canonical ledgers with (zV)^N/N! (continuous) or binomial (lattice) prefactors; the Ω-sorted gc_thermodynamic_stats reweights an Ω-ladder at its run chemical potential. Reconciling the ω0/tail conventions across them is a recorded follow-up item and out of scope here.
Arguments
df::DataFrame: NS output with columns[:iter, :emax, :num_particles, :log_compression].V::typeof(1.0u"Å^3"): The cell volume.atomic_mass::typeof(1.0u"u"): The particle mass entering Λ(T).reference_activity::typeof(1.0u"Å^-3"): The run's reference activity z0 (must match!).μ_grid::AbstractVector{<:typeof(1.0u"eV")}: Chemical potential grid (Unitful, eV), matching the atomisticgc_thermodynamic_stats_fixed_Nconvention.T_grid::AbstractVector{<:Unitful.Temperature}: Temperature grid (Unitful).ω0::Float64=1.0: Total prior mass before the first cull. Leave at 1.0 for ledgers produced by the driver; do not pass the iteration-based route's(K+1)/K.live_emax::Union{Nothing,Vector{Float64}}=nothing: Energies of the surviving live walkers.live_numbers::Union{Nothing,Vector{Int}}=nothing: Particle counts of the surviving live walkers.observable_cols::AbstractVector{Symbol}=Symbol[]: Names of extra per-dead-pointdfcolumns whose reweighted averages are returned inobservables.live_observables=nothing: Required wheneverobservable_colsis non-empty and a live-set tail is supplied; same contract as the lattice method above.kb::Float64: Boltzmann constant (default: eV/K).n_max::Union{Nothing,Int}=nothing: The run's particle-number cap, for bounded constructions.nothing(the default) selects the unboundede^{z0V}normalization.
Returns
A NamedTuple with the same shape as the lattice method: logXi, mean_N, var_N, mean_U, N_eff, var_U, cov_UN (each a Matrix{Float64} of size (length(μ_grid), length(T_grid)), indexed [i_μ, i_T]), p_N (an Array{Float64,3} indexed [i_μ, i_T, i_N], the particle-number distribution P(N | μ, T) over N_support::UnitRange{Int}, with exactly 0.0 on support members no sample visited), and observables (Dict{Symbol,Matrix{Float64}}). The returned moments are configurational: beyond the Λ(T)³ activity conversion this reduction carries no momentum integral, so temperature-derivative observables built from it (heat capacities) must add the Λ(T) term explicitly, exactly as for gc_thermodynamic_stats_fixed_N.
FreeBird.AnalysisTools.inflection_transitions — Method
inflection_transitions(df::DataFrame, n_walkers::Int;
n_cull=1, max_order=2, n_nodes=400, halfwidth=0,
kb=8.617333262e-5, prominence=0.20, prominence_abs=nothing, edge=4,
ground_trim=0.05, min_separation=nothing, energy_window=nothing,
beta_max=nothing)Identify and classify phase transitions in a nested-sampling output df by microcanonical inflection-point analysis (Schnabel et al. 2011; Qi & Bachmann 2018). Returns one entry per transition, ordered by energy.
A transition of order n is a "least-sensitive" inflection point of the entropy, detected as an extremum of the n-th derivative Sⁿ(E) with a Qi–Bachmann sign signature. Independent transitions: odd order → a positive local minimum of Sⁿ (order 1 = β backbending, first-order), even order → a negative local maximum (order 2 = γ peak, second-order). Dependent transitions carry the swapped signatures (odd order → negative local maximum, even order → positive local minimum) and are reported only when accompanied by an independent transition of lower order at lower energy — Qi & Bachmann's necessary condition. Orders 1…max_order are searched. The transition temperature is T_tr = 1/(kb·β(E_tr)).
Arguments
max_order: highest transition order to search (1–3).kb: Boltzmann constant; default8.617333262e-5eV/K givesT_trin Kelvin when:emaxis in eV. Passkb=1.0for the microcanonical temperature in energy units (kT).prominence: peak prominence threshold as a fraction of the robust (10–90 percentile) range of the linearly detrended derivative over the analysis window (default 0.20). Detrending ties the threshold to the size of the transition features rather than to how much ladder is analyzed. Raise it to keep only strong transitions.prominence_abs: absolute prominence threshold in the units ofSⁿ; when set, it replaces the relativeprominencecriterion (defaultnothing).ground_trim: fraction of the energy span (above the ground state) to discard before differentiating, removing theβ → ∞divergence asdE/di → 0(default 0.05). γ/δ are recomputed on the trimmed window so transitions are not contaminated by the divergence.edge,min_separation,energy_window,beta_max: numerical controls;edge ≥ 1nodes are skipped at each window boundary,min_separationdefaults to 3% of the (trimmed) energy span, andbeta_maxoptionally adds an explicit β ceiling. Nodes removed bybeta_max(or theβ > 0filter) can split the window into disjoint energy segments; derivatives and extrema are then computed per contiguous segment, never across a gap.n_cull,n_nodes,halfwidth: as incaloric_derivatives.
Returns
Vector{NamedTuple} with fields E_tr, T_tr, order::Int, kind (:independent or :dependent, assigned from the sign signatures above), and strength (the Sⁿ extremum value).
γ and higher derivatives are sensitive to NS statistics. The ladder roughness is finite-walker granularity (it does not average out over seeds), so use enough walkers K; verify with transition_convergence. Heavy smoothing (large halfwidth) on under-converged ladders biases T_tr. On lattice ladders with exactly (or near-exactly) degenerate levels, plateau stretches of emax are dropped by an internal slope floor before β is formed; a plateau longer than the smoothing window leaves a gap in the analyzed energy range.
FreeBird.AnalysisTools.internal_energy — Method
internal_energy(β::Float64, ωi::Vector{Float64}, ei::Vector{Float64})Calculates the internal energy from the partition function for the given $\beta$, $\omega$ factors, and energies. The internal energy is defined as:
\[U(\beta) = \frac{\sum_i \omega_i E_i \exp(-E_i \beta)}{\sum_i \omega_i \exp(-E_i \beta)}\]
where $\omega_i$ is the $i$-th $\omega$ factor, $E_i$ is the $i$-th energy, and $\beta$ is the inverse temperature.
Arguments
β::Float64: The inverse temperature.ωi::Vector{Float64}: The $\omega$ factors.Ei::Vector{Float64}: The energies in eV.
Returns
- The internal energy.
FreeBird.AnalysisTools.kish_effective_sample_size — Method
kish_effective_sample_size(log_weights::AbstractVector{<:Real})Computes the Kish effective sample size
\[N_\mathrm{eff} = \frac{\left(\sum_i w_i\right)^2}{\sum_i w_i^2}\]
of a weighted sample from its log-weights. The maximum log-weight is subtracted before exponentiation, so a common additive constant in the log-weights (a common scale factor in the weights, such as an ω0 convention) cannot overflow or underflow the ratio, and weight collections spanning hundreds of nats — where linear-space weights underflow to 0.0 — are handled without loss.
The value ranges from 1 (a single weight dominates) to length(log_weights) (all weights equal) and measures how many equally-weighted samples the weighted collection is worth for estimator-variance purposes. Entries of -Inf (zero weights) are legal and drop out of both sums, so the returned value is the effective sample size of the surviving finite-weight entries.
Arguments
log_weights::AbstractVector{<:Real}: Natural logs of the (unnormalized) sample weights. Must be non-empty, must not containNaNor+Inf, and must contain at least one finite entry.
Returns
- The Kish effective sample size, a
Float64in[1, length(log_weights)].
FreeBird.AnalysisTools.microcanonical_entropy — Method
microcanonical_entropy(df::DataFrame, n_walkers::Int;
n_cull=1, kind=:caloric, n_nodes=400, halfwidth=0)Estimate the microcanonical entropy $S(E)$ from a canonical nested-sampling output df (columns :iter, :emax) produced with n_walkers live points.
Along the contiguous cull index $i$ the enclosed prior volume is $X_i=(K/(K+n_{cull}))^i$, so the volume entropy is $S_{vol}=\ln X_i=i\ln(K/(K+n_{cull}))$ and the caloric entropy is $S(E)=\ln g(E)=i\ln(K/(K+n_{cull}))-\ln|dE/di|$, with the ladder slope $dE/di$ obtained from local cubic fits against the dense, uniform i-axis.
Arguments
kind::caloricfor $S=\ln g$ (matches Schnabel/Qi–Bachmann; default) or:volumefor the Hertz/Gibbs volume entropy $S_{vol}=\ln G$ (one fewer derivative, slightly cleaner).n_cull: NS culls per iteration (default 1).n_nodes: number of smoothed output nodes (default 400).halfwidth: half-window (in iterations) of the local cubic smoother;0(default) picksclamp(n÷55, 25, 2000), which requires a ladder of ≥ 55 recorded iterations. Explicit values must be ≥ 2; pass a small one to analyze shorter ladders.
Returns
(E, S) — energies (ascending, same units as :emax) and entropy (dimensionless, up to an additive constant).
See also caloric_derivatives, inflection_transitions.
FreeBird.AnalysisTools.partition_function — Method
partition_function(β::Float64, ωi::Vector{Float64}, Ei::Vector{Float64})Calculates the partition function for the given $\beta$, $\omega$ factors, and energies. The partition function is defined as:
\[Z(\beta) = \sum_i \omega_i \exp(-E_i \beta)\]
where $\omega_i$ is the $i$-th $\omega$ factor, $E_i$ is the $i$-th energy, and $\beta$ is the inverse temperature.
Arguments
β::Float64: The inverse temperature.ωi::Vector{Float64}: The $\omega$ factors.Ei::Vector{Float64}: The energies.
Returns
- The partition function.
FreeBird.AnalysisTools.read_output — Method
read_output(filename::String)Reads the output file and returns a DataFrame.
FreeBird.AnalysisTools.reference_activity_temperature — Method
reference_activity_temperature(reference_activity::typeof(1.0u"Å^-3"),
atomic_mass::typeof(1.0u"u")) -> typeof(1.0u"K")The temperature at which the reference activity equals the inverse cubed thermal de Broglie wavelength, z0 Λ(T)^3 = 1, that is k_B T = h^2 z0^(2/3) / (2π m). It is the zero of the ideal-gas-referenced window centre μ_ref(T) = k_B T ln(z0 Λ(T)^3) and, for a run ordered by Ω = E − μN at its own μ (the chemical_potential field of AtomisticIGRefGCNSParameters), the temperature at which the reweighting factor of gc_thermodynamic_stats_ideal_ref depends on a shell through Ω alone, the residual (z0 Λ(T)^3)^{-N} being identically one, so that pooling the particle-number sectors is exact there. For any target temperature T the factor is a function of Ω alone along the line μ' = μ + k_B T ln(z0 Λ(T)^3); at fixed μ' = μ the residual is (z0 Λ(T)^3)^{-N} per shell, of order (1e5)^N for argon-mass particles at z0V of order unity and room temperature, where this temperature itself is sub-kelvin. Report it alongside every such run. Evaluated through _thermal_wavelength, so it shares the reductions' constants digit for digit.
FreeBird.AnalysisTools.transition_convergence — Method
transition_convergence(dfs, n_walkers; tol=0.1, match_tol=0.25, kwargs...)Assess walker-count (K) convergence of microcanonical inflection-point transitions. dfs is a collection of nested-sampling outputs at the walker counts n_walkers (any order; they are sorted ascending internally). inflection_transitions is run at each K and the results from the largest K are taken as the current best estimate, then traced down the K-ladder.
This is the recommended way to use the inflection analysis: the γ (and higher) derivatives carry finite-K "staircase" granularity that does not average out over independent runs, so a transition should only be trusted once its temperature and order stop drifting as K increases. Near-ground transitions converge last.
Arguments
tol: relativeT_trdrift between the two largestKbelow which a transition is flaggedconverged(default 0.1).match_tol: relativeT_trwindow for matching the same transition acrossK(default 0.25).kwargs...: forwarded toinflection_transitions(e.g.max_order,prominence,ground_trim,kb).
Returns
Vector{NamedTuple}, one per transition found at the largest K, with fields T_tr, order, kind, converged::Bool, T_drift (relative change from the second-largest K, Inf if it has no match there), T_by_K (the matched T_tr at each K, missing where unmatched), and n_walkers (the sorted K values).
FreeBird.AnalysisTools.ωᵢ — Method
ωᵢ(log_compression::AbstractVector{Float64}; ω0::Float64=1.0)Calculates the $\omega$ factors (per-row prior-mass shells) from a ledger's per-cull log-compression column, as recorded by the serial atomistic nested_sampling_step! methods. Row i's shell is
\[\omega_i = X_{i-1} - X_i, \qquad X_i = \omega_0 \prod_{j \le i} t_j,\]
where $t_j = \exp(\texttt{log\_compression}_j)$ is the compression factor charged for cull $j$ (n/(n+1) for an ordinary cull from n live walkers, (n-1)/n for a plateau tie evicted without replacement). For a tie-free fixed-K ledger the column is uniformly log(K/(K+1)) and this method with the default ω0 = 1.0 agrees with the iteration-based method as ωᵢ(1:n, K; ω0=(K+1)/K) up to floating-point evaluation order: here ω0 is the total prior mass before the first cull, whereas the (K+1)/K factor conventionally passed to the iteration-based method only undoes that method's iteration-count offset — do not pass it here. Ledgers containing plateau tie blocks REQUIRE this method, since the iteration-based weights assume a constant per-cull compression and over-compress plateaus.
Arguments
log_compression::AbstractVector{Float64}: The ledger's per-cull log-compression values, in row order.ω0::Float64: The total prior mass before the first cull. Default is 1.0, which is correct for ledgers produced bynested_sampling; the summed shells then equal1 - exp(sum(log_compression)), with the remainder being the live-set tail.
Returns
- A vector of $\omega$ factors.
FreeBird.AnalysisTools.ωᵢ — Method
ωᵢ(iters::Vector{Int}, n_walkers::Int; n_cull::Int=1, ω0::Float64=1.0)Calculates the $\omega$ factors for the given number of iterations and walkers. The $\omega$ factors account for the fractions of phase-space volume sampled during each nested sampling iteration, defined as:
\[\omega_i = \frac{C}{K+C} \left(\frac{K}{K+C}\right)^i\]
where $K$ is the number of walkers, $C$ is the number of culled walkers, and $i$ is the iteration number.
Arguments
iters::Vector{Int}: The iteration numbers.n_walkers::Int: The number of walkers.n_cull::Int: The number of culled walkers. Default is 1.ω0::Float64: The initial $\omega$ factor. Default is 1.0.
Returns
- A vector of $\omega$ factors.