Mesh Preparation

Cocoa uses unstructured triangular meshes stored in NetCDF format following UGRID conventions. This document describes the mesh format and how to prepare meshes for Cocoa simulations.

Mesh Requirements

Cocoa meshes must satisfy the following requirements:

  • Element Type: Linear triangular elements only

  • Connectivity: Properly connected mesh with no isolated nodes

  • Quality: Reasonable element aspect ratios and low (<8) valency

  • Numbering: Consistent counter-clockwise node ordering

  • Format: NetCDF with UGRID conventions

Mesh Format

Cocoa uses a NetCDF format based on UGRID conventions. The format stores mesh geometry, boundaries, and optional nodal attributes in a single file.

Note

Cocoa does not directly read ADCIRC fort.14 files. Use the provided convert_adcirc_format.py script to convert ADCIRC meshes to Cocoa format. See Converting from ADCIRC Format below.

Dimensions

Dimension

Description

nnode

Number of mesh nodes

nface

Number of mesh elements (faces/triangles)

max_face_nodes

Maximum nodes per face (always 3 for triangles)

nboundary

Number of boundary segments

nboundary_node

Total number of boundary nodes across all segments

Required Variables

Mesh Topology:

Variable

Dimensions

Description

adcirc_mesh

(scalar)

Mesh topology variable with UGRID attributes

x

(nnode)

Node x-coordinates (longitude for spherical) [degrees_east]

y

(nnode)

Node y-coordinates (latitude for spherical) [degrees_north]

depth

(nnode)

Bathymetric depth at each node [m, positive down]

mesh_face_nodes

(nface, 3)

Element connectivity (0-based node indices)

Boundary Variables:

Variable

Dimensions

Description

boundary_types

(nboundary)

ADCIRC boundary type code (-1=open, 0/1=land, etc.)

boundary_start_index

(nboundary)

Start index into boundary_nodes for each segment

boundary_end_index

(nboundary)

End index into boundary_nodes for each segment

boundary_nodes

(nboundary_node)

Node indices for all boundary nodes

boundary_pair_node

(nboundary_node)

Paired node for internal barriers (weir/levee)

boundary_elevation

(nboundary_node)

Barrier crest elevation [m]

boundary_supercritical_coefficient

(nboundary_node)

Supercritical flow coefficient

boundary_subcritical_coefficient

(nboundary_node)

Subcritical flow coefficient

Nodal Attributes

Cocoa supports spatially-varying physics parameters through nodal attributes stored in the mesh file. These allow different parameter values at each node, enabling more realistic representation of physical processes.

Available Nodal Attributes

Variable Name

Units

Default

Description

mannings_n_at_sea_floor

s/m^(1/3)

0.025

Manning’s n roughness coefficient for bottom friction

primitive_weighting_in_continuity_equation

1/s

0.03

GWCE weighting parameter (tau0)

surface_submergence_state

(unitless)

0.0

Initial wet/dry state (0=wet, 1=dry, matches ADCIRC startDry)

surface_directional_effective_roughness_length

m

0.0

Directional land roughness lengths for wind reduction (12 values per node)

surface_canopy_coefficient

(unitless)

1.0

Canopy coefficient for wind sheltering (0=full sheltering, 1=no effect)

Manning’s n (mannings_n_at_sea_floor)

The Manning’s n coefficient controls bottom friction. Spatially-varying values allow representation of different bottom types:

  • Sandy bottoms: 0.015-0.025

  • Gravel: 0.025-0.035

  • Vegetated areas: 0.035-0.070

  • Channels with vegetation: 0.05-0.15

Configuration:

To use spatially-varying Manning’s n from the mesh file:

physics:
  manning_n: mesh    # Read from mesh file

To use a constant value instead:

physics:
  manning_n: 0.025   # Constant value for entire domain

Tau0 (primitive_weighting_in_continuity_equation)

The tau0 parameter controls the GWCE formulation weighting between the primitive continuity equation and the wave equation form:

  • tau0 = 0: Pure primitive continuity equation (mass conservation)

  • tau0 > 0: Wave equation form (improved stability)

Typical values range from 0.001 to 0.03. Lower values are often used near open boundaries to reduce reflections.

Configuration:

To use spatially-varying tau0 from the mesh file:

physics:
  tau0: mesh    # Read from mesh file

To use a constant value instead:

physics:
  tau0: 0.005   # Constant value for entire domain

Surface Submergence State (surface_submergence_state)

This attribute specifies the initial wet/dry state of each node, matching ADCIRC’s startDry nodal attribute convention. This is useful for hotstart simulations or when the initial water level would leave some areas dry.

  • 0 = Node starts wet (default)

  • 1 = Node starts dry

Behavior:

When this attribute is present in the mesh file, Cocoa will:

  1. First check depth-based drying (nodes with depth + initial_zeta <= h0)

  2. Then apply submergence state (nodes with value=1 are set dry)

  3. Initialize element active status based on nodal states

  4. Check for landlocked nodes (wet nodes with no active elements)

  5. Set dry node elevations to minimum depth (zeta = h0 - depth)

If this attribute is absent, only depth-based and landlocked checks apply.

Directional Effective Roughness (surface_directional_effective_roughness_length)

This attribute provides land surface roughness lengths in 12 directional bins (30-degree spacing, starting from East and proceeding counter-clockwise) for each node. It is used by the directional wind reduction algorithm to reduce wind velocity over land based on the upwind roughness characteristics.

This attribute is stored as a 2D variable with shape [num_nodes, 12].

  • 12 components per node: one roughness length per 30-degree directional bin

  • Units: meters

  • Typical values: 0.001 (open water) to 1.0+ (dense forest/urban)

  • Default: 0.0 (disabled)

Configuration:

To use directional roughness from the mesh file:

physics:
  surface_directional_roughness: mesh    # Read 12-directional roughness from mesh

Setting to 0 (default) disables directional wind reduction entirely.

Note

A constant scalar value is not meaningful for this attribute because it requires 12 directional components per node. If a non-zero constant is specified in the configuration, it will be ignored with a warning.

See Wind Reduction for details on the wind reduction algorithm.

Surface Canopy Coefficient (surface_canopy_coefficient)

This attribute specifies a per-node multiplicative canopy sheltering factor applied to wind velocity before stress computation. It represents the fraction of free-stream wind that reaches the water surface under forest canopy.

  • 1.0 = no sheltering (open area, default)

  • 0.0 = complete sheltering (no wind reaches surface)

  • Typical forested values: 0.3–0.7

Configuration:

To use spatially-varying canopy coefficients from the mesh file:

physics:
  surface_canopy_coefficient: mesh    # Read from mesh file

To use a constant value instead:

physics:
  surface_canopy_coefficient: 0.5     # 50% reduction everywhere

Setting to 1.0 (default) disables the canopy reduction.

Converting from ADCIRC Format

Cocoa provides a Python script to convert ADCIRC fort.14 mesh files (and optionally fort.13 nodal attribute files) to Cocoa’s NetCDF format.

Script Location

The conversion script is located at:

utils/convert_adcirc_format.py

Usage

Basic conversion (mesh only):

python utils/convert_adcirc_format.py \
    --mesh fort.14 \
    --output mesh.nc

With nodal attributes:

python utils/convert_adcirc_format.py \
    --mesh fort.14 \
    --attributes fort.13 \
    --output mesh.nc

With SAL data:

python utils/convert_adcirc_format.py \
    --mesh fort.14 \
    --attributes fort.13 \
    --sal fort.24 \
    --output mesh.nc

Command-line options:

Option

Required

Description

--mesh

Yes

Path to ADCIRC fort.14 mesh file

--attributes

No

Path to ADCIRC fort.13 nodal attributes file

--output

Yes

Path for output NetCDF file

--sal

No

Path to ADCIRC SAL file (fort.24 ASCII or fort.24.nc NetCDF). Embeds per-node SAL amplitude and phase data into the mesh file.

Requirements:

The script requires Python 3 with the following packages:

  • netCDF4

  • numpy

Install with:

pip install netCDF4 numpy

What Gets Converted

The conversion script transfers:

  • Mesh geometry: Node coordinates (x, y) and bathymetry (depth)

  • Element connectivity: Triangle definitions

  • All boundary types: Open boundaries, land boundaries, weirs, levees, etc.

  • Boundary attributes: Crest elevations, flow coefficients, pipe parameters

  • Nodal attributes: All attributes from fort.13 (Manning’s n, tau0, etc.)

Adding Nodal Attributes

You can add nodal attributes to an existing NetCDF mesh file using Python:

import netCDF4 as nc
import numpy as np

# Open mesh file in append mode
ds = nc.Dataset('mesh.nc', 'a')
num_nodes = len(ds.dimensions['nnode'])

# Add Manning's n attribute (spatially varying)
if 'mannings_n_at_sea_floor' not in ds.variables:
    manning = ds.createVariable('mannings_n_at_sea_floor', 'f8', ('nnode',),
                                fill_value=-9999.0, zlib=True, complevel=2)
    manning.long_name = "Manning's n at sea floor"
    manning.units = "s/m^(1/3)"

# Set values (example: higher in shallow areas)
depth = ds.variables['depth'][:]
manning_values = np.where(depth < 10, 0.035, 0.025)
ds.variables['mannings_n_at_sea_floor'][:] = manning_values

# Add tau0 attribute
if 'primitive_weighting_in_continuity_equation' not in ds.variables:
    tau0 = ds.createVariable('primitive_weighting_in_continuity_equation',
                             'f8', ('nnode',),
                             fill_value=-9999.0, zlib=True, complevel=2)
    tau0.long_name = "tau0 weighting parameter"
    tau0.units = "1/s"

# Set constant tau0
ds.variables['primitive_weighting_in_continuity_equation'][:] = 0.005

ds.close()

Mesh Generation Tools

Recommended tools for generating ADCIRC-format meshes (which can then be converted to Cocoa format):

  • SMS (Surface-water Modeling System)

  • OceanMesh2D (MATLAB/Octave mesh generator)

  • QGIS with mesh plugins

  • Triangle (open-source mesh generator)

  • Gmsh (open-source mesh generator)

Complete NetCDF Format Reference

Below is a complete CDL (NetCDF text format) example showing all variables and attributes in the Cocoa mesh format:

netcdf cocoa_mesh {
dimensions:
    nnode = 8303;
    nface = 14761;
    max_face_nodes = 3;
    nboundary = 45;
    nboundary_node = 1972;

variables:
    // Mesh topology variable (UGRID convention)
    int adcirc_mesh;
        adcirc_mesh:cf_role = "mesh_topology";
        adcirc_mesh:long_name = "Topology data of 2D unstructured mesh";
        adcirc_mesh:topology_dimension = 2;
        adcirc_mesh:node_coordinates = "x y";
        adcirc_mesh:face_node_connectivity = "mesh_face_nodes";
        adcirc_mesh:face_dimension = "nface";

    // Node coordinates
    double x(nnode);
        x:long_name = "x coordinate of mesh nodes";
        x:standard_name = "longitude";
        x:units = "degrees_east";
        x:axis = "X";
        x:_FillValue = -9999.0;

    double y(nnode);
        y:long_name = "y coordinate of mesh nodes";
        y:standard_name = "latitude";
        y:units = "degrees_north";
        y:axis = "Y";
        y:_FillValue = -9999.0;

    // Bathymetry
    double depth(nnode);
        depth:long_name = "z coordinate of mesh nodes";
        depth:standard_name = "z";
        depth:units = "meters";
        depth:positive = "down";
        depth:axis = "Z";
        depth:_FillValue = -9999.0;

    // Element connectivity (0-based)
    int mesh_face_nodes(nface, max_face_nodes);
        mesh_face_nodes:long_name = "nodes of faces";
        mesh_face_nodes:cf_role = "face_node_connectivity";
        mesh_face_nodes:start_index = 0;
        mesh_face_nodes:_FillValue = -9999;

    // Boundary information
    int boundary_types(nboundary);
        boundary_types:long_name = "adcirc boundary type code";
        boundary_types:standard_name = "boundary_types";
        boundary_types:_FillValue = -9999;

    int boundary_start_index(nboundary);
        boundary_start_index:long_name = "start index of boundary nodes";
        boundary_start_index:_FillValue = -9999;

    int boundary_end_index(nboundary);
        boundary_end_index:long_name = "end index of boundary nodes";
        boundary_end_index:_FillValue = -9999;

    int boundary_nodes(nboundary_node);
        boundary_nodes:long_name = "boundary nodes data";
        boundary_nodes:_FillValue = -9999;

    int boundary_pair_node(nboundary_node);
        boundary_pair_node:long_name = "boundary pair node data";
        boundary_pair_node:_FillValue = -9999;

    double boundary_elevation(nboundary_node);
        boundary_elevation:long_name = "boundary elevation above datum";
        boundary_elevation:_FillValue = -9999.0;

    double boundary_supercritical_coefficient(nboundary_node);
        boundary_supercritical_coefficient:long_name = "boundary supercritical coefficient";
        boundary_supercritical_coefficient:_FillValue = -9999.0;

    double boundary_subcritical_coefficient(nboundary_node);
        boundary_subcritical_coefficient:long_name = "boundary subcritical coefficient";
        boundary_subcritical_coefficient:_FillValue = -9999.0;

    // Optional: Nodal attributes
    double mannings_n_at_sea_floor(nnode);
        mannings_n_at_sea_floor:long_name = "Manning's n at sea floor";
        mannings_n_at_sea_floor:units = "s/m^(1/3)";
        mannings_n_at_sea_floor:_FillValue = -9999.0;

    double primitive_weighting_in_continuity_equation(nnode);
        primitive_weighting_in_continuity_equation:long_name = "tau0 weighting";
        primitive_weighting_in_continuity_equation:units = "1/s";
        primitive_weighting_in_continuity_equation:_FillValue = -9999.0;

    double surface_submergence_state(nnode);
        surface_submergence_state:long_name = "Surface submergence state";
        surface_submergence_state:_FillValue = 1.0;

    double surface_directional_effective_roughness_length(nnode, ndir);
        surface_directional_effective_roughness_length:long_name = "Directional effective roughness length";
        surface_directional_effective_roughness_length:units = "m";
        surface_directional_effective_roughness_length:_FillValue = -9999.0;
        // ndir = 12 (30-degree bins starting from East, counter-clockwise)

    double surface_canopy_coefficient(nnode);
        surface_canopy_coefficient:long_name = "Surface canopy coefficient";
        surface_canopy_coefficient:units = "1";
        surface_canopy_coefficient:_FillValue = -9999.0;

// Global attributes
:Conventions = "UGRID-1.0";
:title = "Cocoa mesh file";
:source = "Generated by convert_adcirc_format.py";
}