SWAN Physics Components#

[1]:
from rompy.swan.config import SwanConfigComponents
from rompy.swan.components.physics import (
    PHYSICS,
    GEN3,
    SSWELL,
    ARDHUIN,
    ZIEGER,
    ROGERS,
    NEGATINP,
    WCAPKOMEN,
    WCAPAB,
    QUADRUPL,
    BREAKCONSTANT,
    BREAKBKD,
    JONSWAP,
    COLLINS,
    MADSEN,
    RIPPLES,
)
from rompy.swan.subcomponents.physics import (
    JANSSEN,
    KOMEN,
    WESTHUYSEN,
    ST6,
    ST6C1,
    ST6C2,
    ST6C3,
    ST6C4,
    ST6C5,
)

Wave generation component#

Three wave generation components are available: GEN1, GEN2 and GEN3. Below we demonstrate different uses for the third generation component GEN3

[2]:
# Check the signature of GEN3 (use double question mark GEN3??
# to view all fields info including description and default values)

GEN3?
Init signature:
GEN3(
    *,
    model_type: Literal['gen3'] = 'gen3',
    source_terms: Union[rompy.swan.subcomponents.physics.JANSSEN, rompy.swan.subcomponents.physics.KOMEN, rompy.swan.subcomponents.physics.WESTHUYSEN, rompy.swan.subcomponents.physics.ST6, rompy.swan.subcomponents.physics.ST6C1, rompy.swan.subcomponents.physics.ST6C2, rompy.swan.subcomponents.physics.ST6C3, rompy.swan.subcomponents.physics.ST6C4, rompy.swan.subcomponents.physics.ST6C5] = WESTHUYSEN(model_type='westhuysen', wind_drag='wu', agrow=False, a=0.0015, cds2=None, br=None),
) -> None
Docstring:
Third generation source terms GEN3.

`GEN3 JANSSEN|KOMEN|WESTHUYSEN|ST6 (...) AGROW [a]`

With this command the user indicates that SWAN should run in third-generation mode
for wind input, quadruplet interactions and whitecapping.
Init docstring:
Create a new model by parsing and validating input data from keyword arguments.

Raises ValidationError if the input data cannot be parsed to form a valid model.
File:           /source/csiro/rompy/rompy/swan/components/physics.py
Type:           ModelMetaclass
Subclasses:

[3]:
# GEN3 requires the source_terms field, defined by a subcomponent
# Let's start off by prescribing the WESTHuysen option with default values

WESTHUYSEN?
Init signature:
WESTHUYSEN(
    *,
    model_type: Literal['westhuysen'] = 'westhuysen',
    wind_drag: Literal['wu', 'fit'] = 'wu',
    agrow: bool = False,
    a: Optional[float] = 0.0015,
    cds2: Optional[float] = None,
    br: Optional[float] = None,
) -> None
Docstring:
Westhuysen source terms subcomponent.

`WESTHUYSEN [cds2] [br]`

Nonlinear saturation-based whitecapping combined with wind input of Yan (1987).

Notes
-----
The two arguments are specified in the Appendix C of the User manual but not in the
command description for WESTH in Section 4.5.4. They are also options in the
WCAPPING command. It is not entirely clear if they should/could be specified here.

References
----------
Van der Westhuysen, A.J., M. Zijlema and J.A. Battjes, 2007: Nonlinear
saturation-based whitecapping dissipation in SWAN for deep and shallow water,
Coast. Engng, 54:2, 151-170.
Init docstring:
Create a new model by parsing and validating input data from keyword arguments.

Raises ValidationError if the input data cannot be parsed to form a valid model.
File:           /source/csiro/rompy/rompy/swan/subcomponents/physics.py
Type:           ModelMetaclass
Subclasses:

[4]:
gen = GEN3(
    source_terms=WESTHUYSEN(),
)
print(gen.render())
GEN3 WESTHUYSEN DRAG WU
[5]:
# Options are rendered if specified

gen = GEN3(
    source_terms=WESTHUYSEN(
        wind_drag="wu",
        agrow=True,
        a=0.0015,
        cds2=5.0e-5,
        br=1.75e-3
    ),
)

print(gen.render())
GEN3 WESTHUYSEN cds2=5e-05 br=0.00175 DRAG WU AGROW a=0.0015
[6]:
# The model_type argument must be specified when using a dict / config

gen = GEN3(
    source_terms={
        "model_type": "westhuysen",
        "cds2": 3.0e-5,
    },
)

print(gen.render())
GEN3 WESTHUYSEN cds2=3e-05 DRAG WU
[7]:
# Otherwise an exception is raised

gen = GEN3(
    source_terms={
        "cds2": 3.0e-5,
    },
)
---------------------------------------------------------------------------
ValidationError                           Traceback (most recent call last)
Cell In[7], line 3
      1 # Otherwise an exception is raised
----> 3 gen = GEN3(
      4     source_terms={
      5         "cds2": 3.0e-5,
      6     },
      7 )

File ~/.virtualenvs/rompy/lib/python3.10/site-packages/pydantic/main.py:342, in pydantic.main.BaseModel.__init__()

ValidationError: 1 validation error for GEN3
source_terms
  Discriminator 'model_type' is missing in value (type=value_error.discriminated_union.missing_discriminator; discriminator_key=model_type)

Available source terms#

[8]:
# JANSSEN
gen = GEN3(
    source_terms=JANSSEN(
        cds1=4.5,
    )
)
print(gen.render())

# KOMEN
gen = GEN3(
    source_terms=KOMEN(
        cds2=2.36e-5,
    )
)
print(gen.render())

# ST6
gen = GEN3(
    source_terms=ST6(
        a1sds=4.7e-7,
        a2sds=6.6e-6,
    )
)
print(gen.render())
GEN3 JANSSEN cds1=4.5 DRAG WU
GEN3 KOMEN cds2=2.36e-05 DRAG WU
GEN3 ST6 a1sds=4.7e-07 a2sds=6.6e-06 UP HWANG VECTAU U10PROXY windscaling=32.0

ST6 presets#

ST6 is defined by several tuning coefficients. The SWAN manual provides calibration combinations that are represented by preset subcomponents ST6Cn, n=1,2,3,4,5

[9]:
for phys in [ST6C1, ST6C2, ST6C3, ST6C4, ST6C5]:
    gen = GEN3(source_terms=phys())
    print(gen.render())
GEN3 ST6 a1sds=4.7e-07 a2sds=6.6e-06 p1sds=4.0 p2sds=4.0 UP HWANG VECTAU U10PROXY windscaling=28.0 AGROW a=0.0015
GEN3 ST6 a1sds=4.7e-07 a2sds=6.6e-06 p1sds=4.0 p2sds=4.0 UP FAN VECTAU U10PROXY windscaling=28.0 AGROW a=0.0015
GEN3 ST6 a1sds=2.8e-06 a2sds=3.5e-05 p1sds=4.0 p2sds=4.0 UP HWANG VECTAU U10PROXY windscaling=32.0 AGROW a=0.0015
GEN3 ST6 a1sds=2.8e-06 a2sds=3.5e-05 p1sds=4.0 p2sds=4.0 UP HWANG VECTAU U10PROXY windscaling=32.0 DEBIAS cdfac=0.89 AGROW a=0.0015
GEN3 ST6 a1sds=6.5e-06 a2sds=8.5e-05 p1sds=4.0 p2sds=4.0 UP HWANG VECTAU U10PROXY windscaling=35.0 DEBIAS cdfac=0.89 AGROW a=0.0015

Swell dissipation and negative wind component#

Three components are available to define swell dissipation SSWELL: ZIEGER, ARDHUIN and ROGERS

[10]:
sswell = ZIEGER(b1=0.00025)
print(sswell.render())

sswell = ARDHUIN(cdsv=1.2)
print(sswell.render())

sswell = ROGERS(cdsv=1.2, feswell=0.01)
print(sswell.render())
SSWELL ZIEGER b1=0.00025
SSWELL ARDHUIN cdsv=1.2
SSWELL ROGERS cdsv=1.2 feswell=0.01

The negative wind input can be prescribed using NEGATINP (note it is only intended to use in conjuction with SSWELL ZIEGER)

[11]:
negatinp = NEGATINP(rdcoef=0.1)
print(negatinp.render())
NEGATINP rdcoef=0.1

Whitecapping components#

Two options available: Komen’s whitecapping WCAPKOMEN and Alves and Banner’s whitecapping WCAPAB

[12]:
wcap = WCAPKOMEN(
    cds2=2.36e-5,
    stpm=3.02e-3,
    powst=2,
    delta=1,
    powk=1,
)
print(wcap.render())

wcap = WCAPAB(
    cds2=5.0e-5,
    br=1.75e-3,
    current=True,
    cds3=0.8,
)
print(wcap.render())
WCAPPING KOMEN cds2=2.36e-05 stpm=0.00302 powst=2.0 delta=1.0 powk=1.0
WCAPPING AB cds2=5e-05 br=0.00175 CURRENT cds3=0.8

Quadruplets component#

Use the QUADRUPL component

[13]:
quadrupl = QUADRUPL()
print(quadrupl.render())

quadrupl = QUADRUPL(
    iquad=2,
    lambd=0.25,
    cn14=3.0e7,
    csh1=5.5,
    csh2=0.833333,
    csh3=-1.25,
)
print(quadrupl.render())
QUADRUPL
QUADRUPL iquad=2 lambda=0.25 Cn14=30000000.0 Csh1=5.5 Csh2=0.833333 Csh3=-1.25

Wave breaking components#

Two options are currently available: constant breaking BREAKCONSTANT and variable breaking BREAKBKD

[14]:
breaking = BREAKCONSTANT(
    alpha=1.0,
    gamma=0.73,
)
print(breaking.render())

breaking = BREAKBKD(
    alpha=1.0,
    gamma0=0.54,
    a1=7.59,
    a2=-8.06,
    a3=8.09
)
print(breaking.render())
BREAKING CONSTANT alpha=1.0 gamma=0.73
BREAKING BKD alpha=1.0 gamma0=0.54 a1=7.59 a2=-8.06 a3=8.09

Bottom friction components#

The following formulations are supported: JONSWAP, COLINS, MADSEN, RIPPLES.

[15]:
# Jonswap
fric = JONSWAP(cfjon=0.38)
print(fric.render())

# Collins
fric = COLLINS(cfw=0.015)
print(fric.render())

# Madsen
fric = MADSEN(kn=0.05)
print(fric.render())

fric = RIPPLES(s=2.65, d=0.0001)
print(fric.render())
FRICTION JONSWAP CONSTANT cfjon=0.38
FRICTION COLLINS cfw=0.015
FRICTION MADSEN kn=0.05
FRICTION RIPPLES S=2.65 D=0.0001

PHYSICS group component#

Defining physics settings in SWAN is somewhat complex in that some options can be intrinsically related. For example, the negative wind input NEGATINP is only intended to use with SSWELL ZIEGER and the swell dissipation SSWELL is expected to be defined alongside the ST6 source terms.

The PHYSICS group component is a convenience to allow prescribing all physics commands as a group and validate them to ensure the inputs are sound. The SwanConfigComponents config class uses the PHYSICS group components to define the model config.

[16]:
PHYSICS?
Init signature:
PHYSICS(
    *,
    model_type: Literal['physics'] = 'physics',
    gen: rompy.swan.components.physics.GEN1 | rompy.swan.components.physics.GEN2 | rompy.swan.components.physics.GEN3 = GEN3(model_type='gen3', source_terms=WESTHUYSEN(model_type='westhuysen', wind_drag='wu', agrow=False, a=0.0015, cds2=None, br=None)),
    sswell: rompy.swan.components.physics.ROGERS | rompy.swan.components.physics.ARDHUIN | rompy.swan.components.physics.ZIEGER | None = None,
    negatinp: Optional[rompy.swan.components.physics.NEGATINP] = None,
    wcapping: rompy.swan.components.physics.WCAPKOMEN | rompy.swan.components.physics.WCAPAB | None = None,
    quadrupl: Optional[rompy.swan.components.physics.QUADRUPL] = None,
    breaking: rompy.swan.components.physics.BREAKCONSTANT | rompy.swan.components.physics.BREAKBKD | None = None,
    friction: rompy.swan.components.physics.JONSWAP | rompy.swan.components.physics.COLLINS | rompy.swan.components.physics.MADSEN | rompy.swan.components.physics.RIPPLES | None = None,
) -> None
Docstring:
Physics group component.

The physics group component is a convenience to allow specifying several individual
components in a single command and check for consistency between them.

TODO: Implement OFF command
Init docstring:
Create a new model by parsing and validating input data from keyword arguments.

Raises ValidationError if the input data cannot be parsed to form a valid model.
File:           /source/csiro/rompy/rompy/swan/components/physics.py
Type:           ModelMetaclass
Subclasses:

[17]:
phys = PHYSICS(
    gen=gen,
    sswell=sswell,
    negatinp=negatinp,
    wcapping=wcap,
    quadrupl=quadrupl,
    breaking=breaking,
    friction=fric,
)
print(phys.render())
WARNING:rompy.swan.components.physics:The negative wind input NEGATINP is only intended to use with the swell dissipation SSWELL ZIEGER but SSWELL ROGERS has been specified.
GEN3 ST6 a1sds=6.5e-06 a2sds=8.5e-05 p1sds=4.0 p2sds=4.0 UP HWANG VECTAU U10PROXY windscaling=35.0 DEBIAS cdfac=0.89 AGROW a=0.0015
SSWELL ROGERS cdsv=1.2 feswell=0.01
NEGATINP rdcoef=0.1
WCAPPING AB cds2=5e-05 br=0.00175 CURRENT cds3=0.8
QUADRUPL iquad=2 lambda=0.25 Cn14=30000000.0 Csh1=5.5 Csh2=0.833333 Csh3=-1.25
BREAKING BKD alpha=1.0 gamma0=0.54 a1=7.59 a2=-8.06 a3=8.09
FRICTION RIPPLES S=2.65 D=0.0001

Inconsistent definitions in the PHYSICS group component raise WARNING rather than exceptions given the user may intend to configure the model that way. The warning above goes away by setting SSWELL ZIEGER:

[18]:
phys = PHYSICS(
    gen=gen,
    sswell=ZIEGER(),
    negatinp=negatinp,
    wcapping=wcap,
    quadrupl=quadrupl,
    breaking=breaking,
    friction=fric,
)
print(phys.render())
GEN3 ST6 a1sds=6.5e-06 a2sds=8.5e-05 p1sds=4.0 p2sds=4.0 UP HWANG VECTAU U10PROXY windscaling=35.0 DEBIAS cdfac=0.89 AGROW a=0.0015
SSWELL ZIEGER
NEGATINP rdcoef=0.1
WCAPPING AB cds2=5e-05 br=0.00175 CURRENT cds3=0.8
QUADRUPL iquad=2 lambda=0.25 Cn14=30000000.0 Csh1=5.5 Csh2=0.833333 Csh3=-1.25
BREAKING BKD alpha=1.0 gamma0=0.54 a1=7.59 a2=-8.06 a3=8.09
FRICTION RIPPLES S=2.65 D=0.0001

Individual physics components default to None in the PHYSICS group component. They need to be explicitly prescribed in order to be rendered:

[21]:
phys = PHYSICS(
    gen=GEN3(),
    friction=MADSEN(),
)
print(phys.render())
GEN3 WESTHUYSEN DRAG WU
FRICTION MADSEN