========= 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.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-info`` prints the version and the execution-space report a run opens with, then exits; like ``--version`` it 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 ``runtime`` base instead of ``devel``, and the ROCm image on AMD's plain ``dev`` base 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_cocoa`` reported 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 by ``COCOA_ARCH``. - On a GPU build with no device visible (a container started without ``--gpus`` or ``--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-info`` does 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 ``cocoa`` user. - The images add nano, zstd, zip, unzip, rsync, tmux, bc, pv, ripgrep, strace, ``ip``, ``ping``, netcat and the HDF5 command-line tools, and put ``ncdump``, ``nccopy`` and ``ncgen`` on ``PATH``. Fixed ^^^^^ - ``cocoa-plot --help`` ended 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.stations`` writes time series at listed points to ``cocoa_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 through ``include:``. 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 through ``output.stations.partially_wet``: ``nearest_wet_depth`` (the default, unlike ADCIRC, so shoreline stations report data), ``nearest_wet`` or ``fill``. 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_time`` and ``output.global.end_time`` limit time-series output to a window of the run (#69). Slices fall on ``start_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``, ``mode`` and ``variables``. ``output.compression`` stays under ``output`` and covers every output file. A configuration with the old flat keys fails schema validation; to upgrade, indent them under ``global:``. - Output files name their node and station coordinates ``lon`` and ``lat`` instead of ``x`` and ``y``; scripts that read ``x``/``y`` from an output file need the new names. ``ucx``/``ucy`` keep their names and carry the ``eastward_sea_water_velocity``/``northward_sea_water_velocity`` standard names, vector long names say eastward and northward, and ``depth`` is marked ``positive: down``. Mesh and checkpoint files keep ``x``/``y``. Fixed ^^^^^ - On a rotated-pole mesh, ``bpg_effective`` was written in the rotated computational frame. It is now rotated back to geographic east/north like velocity. - A ``simulation.end_time`` with a fractional-second remainder (for example ``01:00:00.300`` at 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_velocity`` selects the nodal wetting test's velocity estimate: ``linear``, ``quadratic`` or ``inertial``, ADCIRC's default, ``directvelWD`` and ``directvelWD`` + ``useHF`` forms (``inertial`` is 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``, and ``from_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 ``-rocm`` images move from ROCm 6.3 to ROCm 7.2, the first series that targets the MI350. - The container's ``cocoa`` command runs the build for the visible GPU when ``COCOA_ARCH`` is unset. It resolves the build on every call, so ``COCOA_ARCH`` now works under ``singularity exec``, ``docker exec`` and a host ``mpirun``. Before, only ``docker run`` and ``singularity run`` honoured it: ``singularity exec`` ran the ``ampere80`` build whatever ``COCOA_ARCH`` said, and an unknown ``COCOA_ARCH`` fell 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`` (formerly ``utils/cocoa_mesh_tools.py``), ``cocoa-ocean3d`` (formerly the separate ``utils/cocoa_ocean3d`` package), and ``cocoa-plot`` (formerly ``utils/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-tools`` and ``cocoa-plot`` instead of ``python utils/cocoa_mesh_tools.py`` and ``python utils/plot_cocoa.py`` (in the containers, instead of ``python3 /opt/cocoa/utils/...``), install ``utils/cocoa_tools`` instead of ``utils/cocoa_ocean3d``, and import ``cocoa_mesh_tools`` rather than loading the script by path. ``cocoa-ocean3d`` now always stamps the mesh id on the forcing file, since the fingerprint code is installed with it. - ``cocoa-mesh-tools from_adcirc`` and ``group_variables`` end 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, for ``group_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 the ``inertial`` form, ADCIRC's ``directvelWD`` + ``useHF``, so a configuration translated from a fort.15 without a ``&wetDryControl`` namelist ran a different wetting rule than ADCIRC did. Set ``wetting_velocity: inertial`` to 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 flat ``cf_netcdf`` or ``owi_netcdf`` meteorological source with more than one entry in ``filenames`` is an error, as it already was in a ``domains`` entry; 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_JOBS`` bounds 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 a ``GallStereographic`` mesh 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 ``cudaMalloc`` and a synchronizing ``cudaFree`` from 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 ``order`` defaults to 3. ``order: 4`` and higher had shared the ``order: 3`` code path, so a default run (which asked for 4) computed the P2+P3 expansion; default results are unchanged. ``order: 4`` now 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_factor`` is 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 a ``NetCDF::NetCDF`` imported 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_limit`` and 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. The ``fort.14`` converter still rejects 4-node elements. Output has ``nvertex = 4``, and ``plot_cocoa.py`` draws 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 binary ``advection_state`` nodal attribute (``mesh``). An element advects only when all of its corner nodes do, matching ADCIRC's ``ADVECTLOCAL``. **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. Set ``advection: true`` to restore the previous behavior. - UGRID connectivity with ``start_index`` 0 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_nodes`` 3 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_index`` values, 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.py`` resolves the fort.13 ``advection_state`` depth 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.py`` and the ``cocoa_ocean3d`` node footprint handle the unused fill slot in four-column connectivity correctly. [0.4.4] - 2026-09-02 -------------------- Changed ^^^^^^^ - **Behavior change:** ``bpg_effective_x/y`` are no longer written by default. Like ``tide_potential``, they are forcing diagnostics that cost full node fields at every output interval. Set ``output.variables.bpg_effective: true`` to write them. [0.4.3] - 2026-09-02 -------------------- Added ^^^^^ - ``forcing.meteorological.vortex.taper_a`` sets 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 ``_FillValue`` attribute 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-partition`` validates 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_potential`` is no longer written by default; it is a diagnostic of the forcing, not a model result. Set ``output.variables.tide_potential: true`` to 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_ratio`` and ``dispersion_cd`` modes. Configured under ``forcing.baroclinic``; the friction tensor closes with a positive-semidefinite eigenvalue clamp. ``bpg_effective_x/y`` are 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.compression`` level 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_time`` is optional when ``checkpoint.restart_file`` is 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_friction`` nodal 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 implicit ``cocoa_config.yaml`` default is gone. ``--partition`` gains 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.py`` runs 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_FUNNELED`` to support this; a run aborts at startup if the MPI build cannot provide it. - Command-line parsing moved to CLI11: ``--option=value`` spellings now work, malformed values fail loudly, and ``--help``/``--version`` follow 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 ``--asyncio`` command-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 into ``utils/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 ``cocoaorg`` Docker 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-partition`` and 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.py`` to 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``), in ``split`` or ``combined`` files. - 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): .. code-block:: bash 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 all`` also renumbers nodes) - optional, recommended See :doc:`user_guide/mesh_preparation` and :doc:`user_guide/cocoa_tools`. Configuration Files """"""""""""""""""" Cocoa uses YAML configuration files instead of ADCIRC's fort.15 format. Key differences: .. list-table:: :header-rows: 1 * - ADCIRC Parameter - Cocoa Equivalent * - DT - ``simulation.time_step`` * - TAU0 - ``physics.tau0`` * - STATIM - ``simulation.start_time`` * - RNDAY - ``simulation.end_time`` .. 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**: .. code-block:: text DT = 2.0 TAU0 = 0.005 RNDAY = 1.0 **Cocoa YAML**: .. code-block:: yaml simulation: time_step: 2s start_time: 2025-01-01 end_time: 2025-01-02 physics: tau0: 0.005