Changelog
All notable changes to Cocoa will be documented in this file.
The format is based on Keep a Changelog, and this project adheres to Semantic Versioning.
[0.8.0] - 2026-10-20
Cocoa now applies ADCIRC’s dynamic water level correction, documented as
data assimilation, and every ramp in the configuration is one block with a
form and a delay. The meteorological activation fades change
spelling, so a configuration that sets domains[i].ramp or
vortex.ramp needs a one-line edit; read Changed before upgrading.
Added
Data assimilation water level correction (
forcing.water_level_correction, Data Assimilation): ADCIRC’s dynamic water level correction, applied as a pseudo atmospheric pressure with an optional mass adjust. The correction comes from a nodal netCDF time series or a CF longitude/latitude grid, ramps in with its own ramp block, raises the open and radiation boundaries’ prescribed levels, and is written as thewater_level_correctionoutput variable (#75).Every ramp block takes
formanddelay.formistanh(the default),linearorsmoothstep, whose slope is zero at both ends (Ramps);delayis a time the ramp holds at 0 before it rises, and withduration: 0a delayed ramp is a step. The spin-up ramps (forcing.ramp,forcing.meteorological.ramp,forcing.flow_boundary.ramp,forcing.baroclinic.rampand the correction’s) take both; the meteorological activation fades takeform(#76).
Changed
The meteorological activation fades are ramp blocks:
domains[i].ramp: 6handvortex.ramp: 6hare nowramp: {duration: 6h}. The scalar spelling fails schema validation with a message showing the block.forcing.ramp.duration: 0means no ramp, as it does in every other ramp block, instead of failing validation.A CF meteorological file whose time
unitsattribute is present but cannot be parsed is an error. A missing attribute still falls back to seconds since the Unix epoch with a warning.Nodal netCDF inputs (baroclinic forcing) refuse a packed variable, a node holding the variable’s fill value and a time axis outside the standard calendar; a product with a NaN missing-value marker reads a fill value as missing, and an infinite value fails the load in every product. Units in metres may be spelled
metersormetres.Without tidal boundary forcing, open boundaries are held at
initial_conditions.water_levelinstead of 0.
[0.7.2] - 2026-10-17
A patch release: smaller GPU images, and a --device-info command that
shows which GPU Cocoa found and which target the build was compiled for. The
model, its configuration and its output files are unchanged from 0.7.1.
Added
cocoa --device-infoprints the version and the execution-space report a run opens with, then exits; like--versionit needs no-i. The container guide uses it to confirm that the GPU is visible and the right build was selected.On a GPU, the startup report names the target the build was compiled for (
Compiled For: sm_70,gfx942) beside the device’s compute capability or GCN architecture.
Changed
The CUDA image runs on NVIDIA’s
runtimebase instead ofdevel, and the ROCm image on AMD’s plaindevbase with rocBLAS, rocSPARSE, rocSOLVER and hipBLASLt added, instead of-complete. The images shrink from 15.8 to 9.8 GB (CUDA) and from 24.5 to 13.6 GB (ROCm).
Fixed
With no GPU visible,
source select_cocoareported the image’s default build as detected from the GPU. It now says whether the build was detected, is the image default or is pinned byCOCOA_ARCH.On a GPU build with no device visible (a container started without
--gpusor--nv), a run aborted at startup with a core dump. It now prints the reason, with a hint to pass the GPU into the container, and exits 1;--device-infodoes the same.
[0.7.1] - 2026-10-17
A container-only patch release; the model, its configuration and its output files are unchanged from 0.7.0.
Changed
The images take their Python dependencies from the cocoa-tools package alone, still pre-fetch the Natural Earth coastlines, and build matplotlib’s font cache for the
cocoauser.The images add nano, zstd, zip, unzip, rsync, tmux, bc, pv, ripgrep, strace,
ip,ping, netcat and the HDF5 command-line tools, and putncdump,nccopyandncgenonPATH.
Fixed
cocoa-plot --helpended with a note that read as if cartopy were not installed.
[0.7.0] - 2026-10-16
Cocoa now writes time series at stations, in a file of their own, and can limit global output to a window of the run. The output configuration and the output files’ metadata change to make room for them, so existing configurations and post-processing scripts need small edits; read Changed before upgrading.
Added
output.stationswrites time series at listed points tococoa_station_output.nc, a CF-1.8 timeSeries file that xarray opens directly (#70). Stations have their own interval (by default the global one), time window and variable selection, and can be listed inline or in a separate file throughinclude:. Every time-series variable of the global file is available. Each station interpolates from the element that contains it, linearly on triangles and bilinearly on quadrilaterals, and the file does not depend on how the mesh is partitioned. A partly wet element follows ADCIRC’s station options throughoutput.stations.partially_wet:nearest_wet_depth(the default, unlike ADCIRC, so shoreline stations report data),nearest_wetorfill. Samples are buffered on the GPU: 200 stations sampled every step of a Katrina run cost 0.7 % of stepping on a V100.output.global.start_timeandoutput.global.end_timelimit time-series output to a window of the run (#69). Slices fall onstart_time + k * step_interval; peak variables and checkpoints ignore the window.
Changed
The global output file’s keys move under
output.global:step_interval,filename,modeandvariables.output.compressionstays underoutputand covers every output file. A configuration with the old flat keys fails schema validation; to upgrade, indent them underglobal:.Output files name their node and station coordinates
lonandlatinstead ofxandy; scripts that readx/yfrom an output file need the new names.ucx/ucykeep their names and carry theeastward_sea_water_velocity/northward_sea_water_velocitystandard names, vector long names say eastward and northward, anddepthis markedpositive: down. Mesh and checkpoint files keepx/y.
Fixed
On a rotated-pole mesh,
bpg_effectivewas written in the rotated computational frame. It is now rotated back to geographic east/north like velocity.A
simulation.end_timewith a fractional-second remainder (for example01:00:00.300at a 1 s step) passed the run-length check, which counted whole seconds, and the run then crashed. It is now rejected at startup.
[0.6.0] - 2026-10-11
Partitioned runs reproduce the serial run, the implicit solver is about a fifth faster on multiple GPUs, the wetting rule can be chosen to match ADCIRC’s, and Cocoa’s Python tools become one installable package. Several defaults and validation rules change; read Changed before upgrading.
Added
physics.wetting_velocityselects the nodal wetting test’s velocity estimate:linear,quadraticorinertial, ADCIRC’s default,directvelWDanddirectvelWD+useHFforms (inertialis the one Global STOFS runs).Wet-balanced partitioning (
mesh.partition.wet_balance):none(the default),initial_condition, or a water level in metres. With a wet set, ParMETIS balances each element’s share of the nodes that start wet alongside element cost, to a 1 % tolerance, so ranks finish their wet/dry work together. The partition cache records the rule that built it; a cache built under a different rule is reused with a warning naming both.cocoa-mesh-tools reorder, andfrom_adcirc --reorder, sort a mesh’s elements by their lowest-numbered node so that GPU warps read nearby node data. On the global STOFS mesh every element kernel ran 1.5 to 2.4 times faster and the time step 16% faster on one V100. By default only element order changes;reorder --renumber-nodes(from_adcirc --reorder all) first renumbers the nodes in reverse Cuthill-McKee order, for meshes whose node numbering is not already bandwidth-ordered. The new mesh id invalidates existing partition caches and checkpoints (see the Element Ordering section of the mesh preparation guide).Container builds for B300 (
blackwell103), RTX PRO 6000 Blackwell and the GeForce RTX 50 series (blackwell120), and AMD Instinct MI350X and MI355X (mi350). The-rocmimages move from ROCm 6.3 to ROCm 7.2, the first series that targets the MI350.The container’s
cocoacommand runs the build for the visible GPU whenCOCOA_ARCHis unset. It resolves the build on every call, soCOCOA_ARCHnow works undersingularity exec,docker execand a hostmpirun. Before, onlydocker runandsingularity runhonoured it:singularity execran theampere80build whateverCOCOA_ARCHsaid, and an unknownCOCOA_ARCHfell back to the default with a warning. An unknown value is now an error.
Changed
Cocoa’s Python tools are one installable package,
cocoa-tools(pip install utils/cocoa_tools), which installs their dependencies and three commands:cocoa-mesh-tools(formerlyutils/cocoa_mesh_tools.py),cocoa-ocean3d(formerly the separateutils/cocoa_ocean3dpackage), andcocoa-plot(formerlyutils/plot_cocoa.py). The containers have it installed. Its version follows Cocoa’s. See the Cocoa Tools page of the user guide.Breaking: the old paths are gone. Run
cocoa-mesh-toolsandcocoa-plotinstead ofpython utils/cocoa_mesh_tools.pyandpython utils/plot_cocoa.py(in the containers, instead ofpython3 /opt/cocoa/utils/...), installutils/cocoa_toolsinstead ofutils/cocoa_ocean3d, and importcocoa_mesh_toolsrather than loading the script by path.cocoa-ocean3dnow always stamps the mesh id on the forcing file, since the fingerprint code is installed with it.cocoa-mesh-tools from_adcircandgroup_variablesend with a summary of the mesh they wrote (counts, extent, depth range, boundaries by type, nodal attributes, SAL constituents, node and element ordering, mesh id) in place of their per-group lines, and warn when elements are left out of GPU order, when nodal attributes Cocoa does not read are written, or, forgroup_variables, when a source variable is not copied.Behavior change: the wetting test defaults to ADCIRC’s default form (
linear). It was previously fixed to theinertialform, ADCIRC’sdirectvelWD+useHF, so a configuration translated from a fort.15 without a&wetDryControlnamelist ran a different wetting rule than ADCIRC did. Setwetting_velocity: inertialto keep the old behavior.Behavior change: configuration parsing is stricter. Non-finite or hexadecimal durations (
"nans","inf","0x10s"), a leading+or whitespace in durations, step intervals and numeric constants, and non-numeric trailing text in numeric constants now fail at startup with the key named. A flatcf_netcdforowi_netcdfmeteorological source with more than one entry infilenamesis an error, as it already was in adomainsentry; it previously read the first file and ignored the rest.Behavior change: meshes with NaN or fill-value depths or coordinates, latitudes beyond 90 degrees, or zero-area triangles are rejected when read, naming the file and element.
The stability check bounds elevation from below as well as above. The wet/dry pass records the lowest elevation of every wet node before it dries, so a downward runaway fails the run whether the node is shallow (and was hidden by drying) or deep.
Behavior change: the Manning friction speed is formed in double precision. Serial results move by about 1e-7; cases with an elevation slope limiter can move more where its thresholds amplify the change.
Checkpoints no longer carry
total_element_area. Earlier checkpoints still restore.A restart that would resume past the end of the run is refused, and the output step counter is 64-bit.
Each integration test runs in its own copy of its fixture directory, so the suite runs in parallel without workdir locks (the sanitizer CI job fell from 133 to 50 minutes).
cocoa_LINK_JOBSbounds concurrent link steps, and the clang-tidy gate uses clang-tidy 22.1.8.
Fixed
Behavior change: the Gall Stereographic projection’s easting carries the Earth radius. Carried over from ADCIRC’s
CYLINDERMAP(ICS = 24), it was in radians while the Gall scale factors, also ADCIRC’s, assume metres, so x-gradients on aGallStereographicmesh were off by a factor of about 6.4e6 and a run diverged within its first steps. It now agrees with the other projections: on the WNAT tide case, Gall and equidistant cylindrical elevations differ by 0.4 % of the signal. A test now checks every projection’s scale factors against the derivatives of its own map.Partitioned runs reproduce the serial run. Every element-to-node wet/dry result is combined over all ranks before any rank reads it, including at cold start; lateral stress is summed over ranks before it is normalized (nodes on a partition cut were off by up to 53%); and the consistent GWCE eliminates open-boundary columns on the rank that computed each entry. On a beach whose tidal front crosses a partition cut, the front previously stalled at the cut (888 wet/dry mismatches, 0.37 m elevation error); it now matches serial to output rounding, and the WNAT implicit case differs from serial by 1.8e-7 instead of 2.6e-4.
A rank that owns no node no longer crashes the consistent GWCE solver’s constructor.
The consistent GWCE matrix and right-hand side use one dry-row verdict; they used different predicates and could disagree.
An owned node in no element (a matrix row with no diagonal) fails at construction instead of silently contributing zero to the elevation penalty. Construction also checks that every triangle entry has a matrix slot and guards the column-pair gather against overflow.
The arm64 CPU base container shipped without Ifpack2’s source headers (CMake under QEMU silently globbed no files); the base build now preloads jemalloc for the configure and fails if headers Cocoa needs are missing.
The installation guide and FAQ gave GCC 10.4 as the minimum compiler, but GCC 10 cannot build Cocoa: its standard library has no floating-point
std::from_chars. The minimum is GCC 11, and CMake now rejects GCC older than 11 and Clang older than 14 at configure time.
Performance
Multi-GPU implicit runs: a 6 h global STOFS run (12.8M nodes) on two H100s takes 87.1 s instead of 111.1 s. The consistent GWCE matrix is fill-completed once and reused, removing a
cudaMallocand a synchronizingcudaFreefrom every solve (about 40,000 of each per rank, now about 450 at startup); ghost exchanges pack straight from the field views into persistent buffers and skip owned rows (exchange kernels on two ranks sharing a V100 fall from 2.0-2.6 ms to under 0.1 ms per step); and wet-balanced partitions and element-sorted meshes keep the two GPUs within about 2% of each other.
[0.5.1] - 2026-09-25
A patch release: three physics fixes, current dependencies and the Trilinos 17.2.1 containers.
Changed
Behavior change: the tidal potential
orderdefaults to 3.order: 4and higher had shared theorder: 3code path, so a default run (which asked for 4) computed the P2+P3 expansion; default results are unchanged.order: 4now sums the Legendre series to degree 11, adding up to 1e-4 m of lunar potential over P3. This departs from ADCIRC on purpose: ADCIRC’s closed form loses the Sun’s contribution to cancellation, while the summed series keeps kernel noise near 2e-16 m for both bodies.Behavior change: a lone parametric storm with no gridded background has no blend ring. The default ring (1.25 times the 34 kt footprint) tapered the field to zero with nothing underneath, removing the outer wind and pressure deficit (Milton lost its field beyond about 800 km compared with ADCIRC). Several storms with no background keep a ring for the hand-off between them, defaulting to
blend_factor: 5.0; runs composed with a gridded background keep 1.25.blend_factoris now optional and resolved from the composition.Containers use Trilinos 17.2.1, which is also the default for the Slurm base build; CPM fetches fmt 12.2.0, valijson 1.1.3, CLI11 2.7.2 and Catch2 3.16.0.
Library headers are included with a prefix relative to
src/(for example#include "cocoa_kernel/..."). Out-of-tree code that included kernel headers unprefixed must update its includes.Every first-party target builds with the project warnings and
-Werror, NetCDF is aNetCDF::NetCDFimported target, and CMake presets replace the configure and ctest flags CI repeated.
Fixed
The rewetting path floored the Manning drag coefficient at the compiled-in
cf_lower_limitand ignored the configured value, so a run that set the key applied two different floors. Runs on the default are unchanged.An out-of-range SAL node id raises an error naming SAL instead of reading past the end, and CF variable names given for a non-CF meteorological source warn instead of being ignored.
A
[[no_unique_address]]member, which nvcc lays out differently in device kernel parameters than the host does, is removed from the momentum element context.Trilinos base images are published under
cocoaorg/trilinos_base, matching the documentation.Documentation corrections, notably that the meteorological reader guide said to return unscaled fields for the pipeline to scale, although nothing downstream scales them.
[0.5.0] - 2026-09-13
Added
Quadrilateral elements. The CG solver accepts bilinear quadrilaterals and hybrid meshes that mix triangles and quadrilaterals in any proportion, in serial and under MPI, with no configuration change beyond the mesh file. Quadrilaterals work throughout: wet/dry, the lumped and consistent GWCE, momentum, baroclinic forcing, checkpoints and output. A quadrilateral mesh is a Cocoa NetCDF file with
max_face_nodes = 4, where a fill value in the fourth column marks a triangle. Each quadrilateral is checked for orientation and convexity at load, and a bad element is reported with the file and the element named. Thefort.14converter still rejects 4-node elements. Output hasnvertex = 4, andplot_cocoa.pydraws quadrilateral faces. On a hybrid global shell (1.66M quadrilaterals, 3.85M triangles), an 18-day tide run’s M2 amplitude differs from the all-triangle shell by 6.2 mm RMS. Triangle-only meshes run the same code as before at the same speed. See the channel validation page.Advection state (
physics.advection): advection may be disabled along the open, flow and radiation boundary strings (off_at_open_boundaries), applied everywhere (true), disabled everywhere (false), or masked per node from the mesh file’s binaryadvection_statenodal attribute (mesh). An element advects only when all of its corner nodes do, matching ADCIRC’sADVECTLOCAL.Behavior change: the default is
off_at_open_boundaries, where advection was previously applied everywhere. Results change in a one-element band inside the open and flow strings; land walls and weirs keep advection. Setadvection: trueto restore the previous behavior.UGRID connectivity with
start_index0 or 1.
Changed
The mesh id is hashed on the canonical topology, so a triangle mesh is the same mesh, with the same checkpoints and partition caches, whether written with
max_face_nodes3 or 4.
Fixed
Behavior change: the specified-flux boundary integral now carries the projection map factor. Before, a prescribed discharge was delivered as Q / cos^m(phi0), which is 1.307 times Q on Mercator at 29 N. The discharge is now exactly Q at any latitude and orientation, for flux and weir boundaries on projected meshes. Internal weir results move by up to 6 mm.
Under MPI, the prescribed discharge depended on the rank count: each partition cut through a flux string dropped half a segment of forcing. Discharge now agrees to 7e-5 at 1 to 4 ranks. Open-boundary tidal values were indexed correctly only when the open boundary was the first group in the file.
The land-boundary no-normal-flow condition uses the physical normal, which is exact on any map projection, not only on an equidistant map.
Duplicate flow
segment_indexvalues, tidal tables longer than the open boundary, boundary string indices outside the node table, 1-based connectivity with a non-negative fill value, and degenerate flux chords were accepted silently. Each is now a startup error.cocoa_mesh_tools.pyresolves the fort.13advection_statedepth thresholds into the binary mask Cocoa reads, instead of copying the depths verbatim, and reports how many nodes lose advection.plot_cocoa.py,plot_partition.pyand thecocoa_ocean3dnode footprint handle the unused fill slot in four-column connectivity correctly.
[0.4.4] - 2026-09-02
Changed
Behavior change:
bpg_effective_x/yare no longer written by default. Liketide_potential, they are forcing diagnostics that cost full node fields at every output interval. Setoutput.variables.bpg_effective: trueto write them.
[0.4.3] - 2026-09-02
Added
forcing.meteorological.vortex.taper_asets the steepness of the vortex blend-ring taper (default 2.0, matching GAHM2026).
Fixed
Behavior change: the parametric vortex blend ring sits at a physical footprint radius: the outermost reported isotach radius, or else where the fitted profile falls to half its peak, but never less than twice the radius to maximum winds. It previously used a bracket key that, where no radius was reported, was the radius to maximum winds itself. That switched the vortex off at its strongest point and left a concentric ring in the peak-wind field. The ring now tapers with GAHM2026’s hyperbolic tangent in place of a linear ramp. Any GAHM run produces different output.
Invest-stage ATCF decks and short lines that are truncated after the quadrant radii now load. Missing trailing columns take the same fallbacks as blank ones. Only lines truncated before the radii are skipped, with a log message.
An isotach radius that lies inside the same line’s radius to maximum winds is declined rather than fitted. Machine-learning cyclone products produce these routinely. A track left with fewer than two usable snapshots is ignored with a warning.
[0.4.2] - 2026-08-27
Changed
Behavior change: GAHM vortex winds resolve each bracketing track snapshot spatially (radial bracket per quadrant, then the azimuthal blend) against its own isotach count, then interpolate the two resolved fields in time, following ADCIRC’s NWS=20 scheme. Previously, when the isotach count changed between snapshots, the radial structure froze for the interval and jumped at the midpoint, and slot indexing could read the next snapshot’s data or run past the end of the track. Results change on every interval; the measured effect on the vortex-only test case is a maximum zeta difference of about 9e-3 m.
Configuration errors are aggregated across all sections. A throw in one section no longer hides errors in the sections after it, so a single run reports the whole list.
CI runs ctest in parallel.
Fixed
GAHM tracks: a skipped snapshot (a (0, 0) genesis or DUMY line) no longer serves as the previous eye of the next leg. It had given the first surviving snapshot a spurious translation speed (52.485 m/s instead of 2.570 m/s on a synthetic track) that was added to its wind field.
A malformed OWI ASCII record is rejected, naming the record. It was previously treated as end of file, so the run used silently truncated forcing.
Output variables declare the fill value their
_FillValueattribute advertises (-99999.0). A partially written trailing record, such as after a crash, previously read as about 9.97e36 rather than as missing.A mesh with a weir boundary that omits a required weir column, or with an optional boundary column present but shorter than the boundary, is rejected with a message naming the column, rather than read out of bounds.
--create-partitionvalidates inputs before reading the mesh, and a failure inside it aborts every MPI rank instead of leaving the peers blocked in a collective until walltime.
Performance
The host mesh keeps element connectivity only; basis functions are recomputed during device upload. This returns about 1037 MB per rank at 3.20M elements. Results are unchanged.
Maximum node valence for the consistent GWCE matrix comes from the existing node-to-element connectivity. Setup at 1.60M nodes drops from about 0.51 s to 0.01 s, and about 50 MB that was held for the whole run is freed.
[0.4.1] - 2026-08-22
Changed
Behavior change:
tide_potentialis no longer written by default; it is a diagnostic of the forcing, not a model result. Setoutput.variables.tide_potential: trueto restore it.Behavior change: peak and extrema accumulators are no longer stored in checkpoints, so peak fields reset on restart and cover exactly the run that reports them. A preserved maximum cannot be retracted, so a single bad step would have carried through every later cycle of a hot-started chain. Continuous peak tracking needs a continuous run.
Fixed
Global meshes no longer develop a seam in elevation and velocity at the antimeridian. Element longitudes are unwrapped only on an actual branch crossing (a span over 180 degrees), not on a negative Jacobian. The old test widened every clockwise-wound antimeridian element to a full revolution and flipped the sign of its gradients; on the 12.8M-node global STOFS mesh, all 3942 crossing elements were affected.
Projected-x differences in the wet/dry head gradient and in land boundary chords are reduced to their shortest equivalent across the branch cut. Previously, nodes next to the cut could never re-wet, and boundary normals there collapsed to due north.
[0.4.0] - 2026-08-15
Added
Baroclinic coupling. Nodal baroclinic pressure gradient forcing with the elemental free-surface correction (Crank-Nicolson centered, ramped), internal-tide wave drag computed from a de-tided velocity (Nycander, Lyard and scalar formulations, with the de-tiding window carried across restarts), and momentum dispersion as an additive friction contribution in
mixed_layer_ratioanddispersion_cdmodes. Configured underforcing.baroclinic; the friction tensor closes with a positive-semidefinite eigenvalue clamp.bpg_effective_x/yare available as inspection output.cocoa_ocean3d, an installable Python preprocessor that turns HYCOM GOFS 3.1 fields into the nodal baroclinic forcing file: TEOS-10 density, column-integrated pressure gradient, mixed-layer and dispersion products, mesh regridding, and download.Sea-ice concentration as a first-class meteorological field: CF NetCDF and GRIB ingestion, composition across multi-domain sources, and attenuation of the wind drag coefficient via the Lupkes (2012) parameterization.
Elevation slope limiter (
numeric.elemental_slope_limiter), an optional post-GWCE pass that replaces zeta at nodes whose surrounding elemental elevation gradient exceeds a per-node threshold with the area-weighted average of the node’s element patch.GAHM2026 conformance for weak and degenerate tracks: ambient pressure from the ATCF RADP column, a clamped Holland B, minimum-strength gates, neighbor fill for unreported quadrant radii instead of fabricated ones, and snapshots with missing scalars skipped with a warning rather than aborting the run. Preprocessed fort.22 input (aswip NWS=19/20) is now rejected.
Configurable output compression (
output.compressionlevel and shuffle); the default is deflate level 1 without shuffle, chosen by measurement.utils/plot_cocoa.py, a single plotter for output, peak and mesh files that discovers the mesh and the plottable variables from the file’s UGRID/CF attributes. Handles global and regional meshes, vector pairs, and frame series or movies; shipped in the containers.Nightly and release container publication for the CPU, CUDA and ROCm families.
Changed
Output field variables are written as single precision. Coordinates, bathymetry, the time axis and checkpoints are unchanged.
simulation.start_timeis optional whencheckpoint.restart_fileis set; the cold-start clock is recovered from the checkpoint, and a configured value that disagrees with it is rejected rather than silently re-anchoring every forcing ramp.Checkpoint format version 4: baroclinic Crank-Nicolson levels and the de-tiding window are persisted, so a restart resumes them mid-window rather than rebuilding them. Version 3 checkpoints are refused.
Checkpoint restart compares mesh identity per section. Node positions, connectivity and boundaries must match; bathymetry, nodal attributes and self-attraction/loading may change between segments.
Profiling regions are nested rather than spelling the hierarchy in labels, and the per-step time-level advance, field extrema and checkpoint write are timed for the first time.
Run progress and the time-to-completion estimate are reported against the work this run performs, so a restart no longer starts at the resumed percentage.
Error and critical messages print from every MPI rank, prefixed with the rank id; lower levels stay rank-0-only.
Fixed
On rotated-pole meshes, wind stress and the baroclinic pressure gradient are rotated into the computational frame, and the spherical metric term in momentum is evaluated from the rotated coordinates. Previously geographic vectors reached the equations untransformed.
MPI runs wrote solver-frame velocity to output; velocity rotation to the geographic frame now applies in serial and distributed runs alike.
Flux boundary state is checkpointed in the mesh’s global ordering, so a checkpoint no longer depends on rank count. Truncated files written by earlier versions are refused.
The
internal_tide_frictionnodal attribute’s column count is validated at load, repairing an out-of-bounds read of a single-column attribute.The JSONL step log is written by one writer, one record per step.
Non-binary canopy values are resolved during fort.13 conversion.
Performance
Output staging is device-resident, packed on device and copied back on a dedicated execution-space instance so the transfer overlaps compute. With single-precision storage, the cost of 2-minute output on a 1.57M-node hurricane case (V100, one simulated day) falls from roughly 26% to 3.3% of runtime, and the file halves in size.
[0.3.1] - 2026-07-27
A maintenance release; no new model capability.
Changed
-i(now also spelled--input) is required and must name an existing file; the implicitcocoa_config.yamldefault is gone.--partitiongains the short spelling-p.
Fixed
Registering more extrema fields than the maximum truncated silently, and a sources/configs size mismatch indexed out of range. Both now throw at the boundary, naming the actual and allowed counts.
cocoa_mesh_tools.pyruns in a container with no username populated.
[0.3.0] - 2026-07-20
Added
GAHM parametric hurricane vortex forcing (
cocoa_vortex), with ATCF b-deck parsing and missing-central-pressure estimation.GRIB2 meteorological forcing via ecCodes (
cocoa_ENABLE_GRIB).Multi-source meteorological composition: self-contained
domains:entries, per-source wind reduction, and per-source activation ramps.Configuration schema validation on valijson (JSON Schema draft-07), with the schema embedded in the binary and reproduced in the user guide.
External YAML inclusion at designated configuration nodes.
Checkpoint format version 3: peaks are preserved across a restart, mesh identity is verified before restoring, and the end-of-run checkpoint is gated on solution stability.
Google Benchmark suite with scaling analysis, compile-gated in CI.
New dependencies: valijson and CLI11 (CPM-fetched); ecCodes (optional, GRIB builds only).
Changed
Asynchronous I/O is now always on. Output is delivered on a background writer thread and meteorological input is pre-read ahead of need, overlapping file access with computation in every run. MPI is initialized with
MPI_THREAD_FUNNELEDto support this; a run aborts at startup if the MPI build cannot provide it.Command-line parsing moved to CLI11:
--option=valuespellings now work, malformed values fail loudly, and--help/--versionfollow standard Unix formatting.Boolean configuration values must be
true/false; the YAML 1.1 spellings are rejected.CF NetCDF pressure units are read from the file and required.
Performance
Explicit solver per-step host stalls eliminated, worth roughly 13-15% throughput on a 1.57M-node hurricane case with meteorology; momentum nodal assembly reduced from three sweeps to one; wet/dry list compaction with no per-step device allocation; cached MPI ghost staging and device-direct boundary forcing scatter.
Removed
The
--asynciocommand-line flag and its modes. Asynchronous output is now unconditional, so there is no mode to select. The dedicated-MPI-I/O-rank mode (--asyncio=mpi) has been removed; all ranks now participate in computation.
Fixed
MPI runs fail fast on rank-local errors instead of hanging peers until walltime.
Land-boundary normals at multi-string junction nodes average the per-string normals (a documented deviation from ADCIRC’s last-wins).
Binary-inexact time steps (e.g. 0.1 s) no longer drop the final step.
[0.2.0b0] - 2026-06-29
Added
Full OWI NWS13 support, with Lagrangian moving-vortex interpolation for moving grids.
Grouped mesh NetCDF format carrying boundaries, nodal attributes and self-attraction/loading in one file, identified by a BLAKE2 content fingerprint (
mesh_id) that is stamped onto model output. The mesh tooling is consolidated intoutils/cocoa_mesh_tools.py.Checkpoint format version 2: timestamped single files, written at the end of a run by default, with a warning when a run would accumulate many of them.
Changed
“Hot start” is now “checkpoint” throughout the configuration and docs.
Container images moved to the
cocoaorgDocker Hub namespace.
Removed
The A/B checkpoint file pair and the FNV-1a mesh topology checksum, superseded by timestamped checkpoints and
mesh_id.
Fixed
OWI uniform-grid detection is de-biased with average spacing, so a grid that is regular within round-off is no longer misread as irregular.
Kokkos and KokkosKernels are found as standalone packages, and the diagnostics-color flag is selected per language, fixing the ROCm build.
Multi-value nodal attributes get their secondary dimension when a mesh is converted to the grouped format.
Performance
The static OWI grid is cached rather than reparsed, regular moving grids are read by corners, and the continuity diagonal solve is fused into one kernel.
[0.1.0] - 2026-06-23
The first documented release of Cocoa, a C++20 re-implementation of ADCIRC’s two-dimensional depth-integrated shallow water model, built on Kokkos and Trilinos.
Added
Solution of the depth-integrated momentum equations and the Generalized Wave Continuity Equation (GWCE) on unstructured triangular meshes, with either an explicit lumped-mass GWCE (
explicit/lumped, the default) or a semi-implicit consistent-mass GWCE solved by Belos conjugate gradient with an Ifpack2 Jacobi preconditioner (implicit/consistent).Execution on NVIDIA (CUDA) and AMD (HIP) GPUs, multi-core CPUs (OpenMP), or serially, selected with
cocoa_BACKEND. Computation is in double precision, with float storage of bandwidth-sensitive fields.MPI domain decomposition using ParMETIS partitions, either created automatically or precomputed with
--create-partitionand reused through--partition.Tidal forcing from harmonic constituents on open boundaries, an astronomical tide potential computed directly from Moon and Sun positions, and self-attraction and loading (SAL) from precomputed data.
Meteorological forcing (wind stress through the Garratt drag law, and atmospheric pressure) from CF NetCDF, OWI ASCII, and OWI NetCDF files, including moving vortex-tracking OWI grids. Wind is reduced by directional roughness length and canopy coefficient nodal attributes.
Open, land, specified-flux, radiation, flux-plus-radiation, external weir and internal weir boundaries, using ADCIRC boundary type codes. Internal weir pairs are supported across partitions.
ADCIRC-style wetting and drying: nodal and elemental drying, nodal wetting and landlocked drying.
Quadratic bottom friction with spatially varying Manning’s n, and
tau0, initial dry state and wind reduction nodal attributes.Selectable map projections and coordinate rotation for global meshes.
NetCDF UGRID mesh input, YAML configuration, and
utils/convert_adcirc_format.pyto convert fort.14/fort.13 files.NetCDF output of elevation, velocity, pressure, wind and tide potential time series, plus peak fields (
zeta_max,velocity_max,pressure_min,wind_max), insplitorcombinedfiles.Hot start from checkpoint files written alternately to an A/B pair.
Asynchronous I/O: a background writer thread delivers output and meteorological input is read ahead of need, so file access overlaps computation. MPI is initialized with
MPI_THREAD_FUNNELED, and a run aborts at startup if the MPI build cannot provide it.Docker images for CPU (x86-64 and ARM64), CUDA and ROCm.
Migration Guide
Migrating from ADCIRC
Converting Mesh Files
Cocoa uses NetCDF format for mesh files instead of ADCIRC’s fort.14/fort.13 format.
Convert them with cocoa-mesh-tools, part of the cocoa-tools package
(pip install utils/cocoa_tools; installed in the containers):
cocoa-mesh-tools from_adcirc \
--mesh fort.14 \
--attributes fort.13 \
--sal fort.24.nc \
--output mesh.nc \
--reorder
Arguments:
--mesh: ADCIRC mesh file (fort.14) - required--attributes: ADCIRC nodal attributes file (fort.13) - optional--sal: self-attraction/loading file (fort.24 or fort.24.nc) - optional--output: Output NetCDF file path - required--reorder: sort elements for GPU performance (--reorder allalso renumbers nodes) - optional, recommended
See Mesh Preparation and Cocoa Tools (Python).
Configuration Files
Cocoa uses YAML configuration files instead of ADCIRC’s fort.15 format. Key differences:
ADCIRC Parameter |
Cocoa Equivalent |
|---|---|
DT |
|
TAU0 |
|
STATIM |
|
RNDAY |
|
Note
In ADCIRC, a negative TAU0 value selects a spatially varying scheme. Cocoa
does not compute those schemes and rejects a negative tau0: set a
non-negative constant, or tau0: mesh to read per-node values from the
mesh’s primitive_weighting_in_continuity_equation nodal attribute.
Example conversion:
ADCIRC fort.15:
DT = 2.0
TAU0 = 0.005
RNDAY = 1.0
Cocoa YAML:
simulation:
time_step: 2s
start_time: 2025-01-01
end_time: 2025-01-02
physics:
tau0: 0.005