Skip to content

SCHISM

Grids

SCHISMGrid

Bases: BaseGrid

SCHISM grid in geographic space.

Source code in rompy_schism/grid.py
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
class SCHISMGrid(BaseGrid):
    """SCHISM grid in geographic space."""

    grid_type: Literal["schism"] = Field("schism", description="Model descriminator")
    hgrid: DataBlob = Field(..., description="Path to hgrid.gr3 file")
    vgrid: Optional[DataBlob | VgridGenerator | VGrid] = Field(
        description="Path to vgrid.in file",
        default_factory=create_2d_vgrid,
    )

    @model_validator(mode="after")
    def validate_gr3_fields(self):
        """Custom validator to handle GR3Generator conversion during deserialization."""
        # Convert GR3Generator fields that might have been serialized as simple values
        gr3_fields = [
            "drag",
            "diffmin",
            "diffmax",
            "albedo",
            "watertype",
            "windrot_geo2proj",
        ]

        for field in gr3_fields:
            value = getattr(self, field, None)
            if (
                value is not None
                and not isinstance(value, (DataBlob, GR3Generator))
                and isinstance(value, (int, float))
            ):
                # Create a GR3Generator instance instead of using the raw value
                gr3_generator = GR3Generator(
                    hgrid=self.hgrid,
                    gr3_type=field,
                    value=value,
                )
                setattr(self, field, gr3_generator)

        return self

    @model_serializer
    def serialize_model(self, **kwargs):
        """Custom serializer to handle proper serialization."""
        result = {}

        # Add fields to the result dictionary
        for field_name in self.model_fields:
            value = getattr(self, field_name, None)

            # Special handling for GR3Generator fields
            if value is not None and isinstance(value, GR3Generator):
                # For GR3Generator objects, just use the value field
                result[field_name] = value.value
            # Skip wwmbnd field if it's a WWMBNDGR3Generator (similar to TimeRange behavior in memory)
            elif (
                field_name == "wwmbnd"
                and value is not None
                and isinstance(value, WWMBNDGR3Generator)
            ):
                # Skip this field to prevent validation errors as it's complex to serialize/deserialize
                pass
            elif value is not None and not field_name.startswith("_"):
                result[field_name] = value

        return result

    drag: Optional[DataBlob | float | GR3Generator] = Field(
        default=None, description="Path to drag.gr3 file"
    )
    rough: Optional[DataBlob | float | GR3Generator] = Field(
        default=None, description="Path to rough.gr3 file"
    )
    manning: Optional[DataBlob | float | GR3Generator] = Field(
        default=None,
        description="Path to manning.gr3 file",  # TODO treat in the same way as the other gr3 files. Add a warning that this is not advisable
    )
    hgridll: Optional[DataBlob | int | GridLinker] = Field(
        default=None,
        description="Path to hgrid.ll file",
        validate_default=True,
    )
    diffmin: Optional[DataBlob | float | GR3Generator] = Field(
        default=1.0e-6,
        description="Path to diffmax.gr3 file or constant value",
        validate_default=True,
    )
    diffmax: Optional[DataBlob | float | GR3Generator] = Field(
        default=1.0,
        description="Path to diffmax.gr3 file or constant value",
        validate_default=True,
    )
    albedo: Optional[DataBlob | float | GR3Generator] = Field(
        default=0.15,
        description="Path to albedo.gr3 file or constant value",
        validate_default=True,
    )
    watertype: Optional[DataBlob | int | GR3Generator] = Field(
        default=1,
        description="Path to watertype.gr3 file or constant value",
        validate_default=True,
    )
    windrot_geo2proj: Optional[DataBlob | float | GR3Generator] = Field(
        default=0.0,
        description="Path to windrot_geo2proj.gr3 file or constant value",
        validate_default=True,
    )
    hgrid_WWM: Optional[DataBlob | GridLinker] = Field(
        default=None,
        description="Path to hgrid_WWM.gr3 file",
        validate_default=True,
    )
    wwmbnd: Optional[DataBlob | WWMBNDGR3Generator] = Field(
        default=None,
        description="Path to wwmbnd.gr3 file",  # This is generated on the fly. Script sent from Vanessa.
        validate_default=True,
    )
    crs: str = Field("epsg:4326", description="Coordinate reference system")
    _pylibs_hgrid: Optional[schism_grid] = None
    _pylibs_vgrid: Optional[object] = None

    @model_validator(mode="after")
    def validate_rough_drag_manning(cls, v):
        fric_sum = sum([v.rough is not None, v.drag is not None, v.manning is not None])
        if fric_sum > 1:
            raise ValueError("Only one of rough, drag, manning can be set")
        if fric_sum == 0:
            raise ValueError("At least one of rough, drag, manning must be set")
        return v

    @field_validator(*G3FILES)
    @classmethod
    def gr3_source_validator(cls, v, values):
        if v is not None:
            if not isinstance(v, DataBlob):
                v = GR3Generator(
                    hgrid=values.data["hgrid"], gr3_type=values.field_name, value=v
                )
        return v

    @field_validator(*GRIDLINKS)
    @classmethod
    def gridlink_validator(cls, v, values):
        if v is None:
            v = GridLinker(hgrid=values.data["hgrid"], gridtype=values.field_name)
        return v

    @field_validator("wwmbnd")
    @classmethod
    def wwmbnd_validator(cls, v, values):
        if v is None:
            v = WWMBNDGR3Generator(hgrid=values.data["hgrid"])
        return v

    @field_validator("vgrid")
    @classmethod
    def vgrid_validator(cls, v, values):
        if v is None:
            v = VgridGenerator()
        return v

    @property
    def x(self) -> np.ndarray:
        return self.pylibs_hgrid.x

    @property
    def y(self) -> np.ndarray:
        return self.pylibs_hgrid.y

    @property
    def ne(self) -> int:
        return self.pylibs_hgrid.ne

    @property
    def np(self) -> int:
        return self.pylibs_hgrid.np

    @property
    def pylibs_hgrid(self):
        if self._pylibs_hgrid is None:
            grid_path = self.hgrid._copied or self.hgrid.source
            try:
                # Try to load as schism_grid first
                self._pylibs_hgrid = schism_grid(grid_path)
            except Exception:
                # Fall back to read_schism_hgrid
                self._pylibs_hgrid = read_schism_hgrid(grid_path)

            # Compute all grid properties to ensure they're available
            if hasattr(self._pylibs_hgrid, "compute_all"):
                self._pylibs_hgrid.compute_all()

            # Calculate boundary information
            if hasattr(self._pylibs_hgrid, "compute_bnd"):
                self._pylibs_hgrid.compute_bnd()

        return self._pylibs_hgrid

    @property
    def pylibs_vgrid(self):
        if self.vgrid is None:
            return None
        if self._pylibs_vgrid is None:
            vgrid_path = self.vgrid._copied or self.vgrid.source
            self._pylibs_vgrid = read_schism_vgrid(vgrid_path)
        return self._pylibs_vgrid

    # Legacy properties for backward compatibility
    @property
    def pyschism_hgrid(self):
        logger.warning("pyschism_hgrid is deprecated, use pylibs_hgrid instead")
        return self.pylibs_hgrid

    @property
    def pyschism_vgrid(self):
        logger.warning("pyschism_vgrid is deprecated, use pylibs_vgrid instead")
        return self.pylibs_vgrid

    @property
    def is_3d(self):
        if self.vgrid is None:
            return False
        elif isinstance(self.vgrid, DataBlob):
            return True
        elif isinstance(self.vgrid, VgridGenerator):
            # Check the vgrid_type attribute of the VgridGenerator
            if self.vgrid.vgrid_type.lower() == VGRID_TYPE_2D:
                return False
            else:
                return True
        # Fallback for any other case (including when accessing the property before initialization)
        return False

    @property
    def nob(self):
        if not hasattr(self.pylibs_hgrid, "nobn"):
            self.pylibs_hgrid.compute_bnd()
        return self.pylibs_hgrid.nob

    @property
    def nobn(self):
        if not hasattr(self.pylibs_hgrid, "nobn"):
            self.pylibs_hgrid.compute_bnd()
        return self.pylibs_hgrid.nobn

    @property
    def nvrt(self):
        if self.is_3d:
            return self.pylibs_vgrid.nvrt
        else:
            return None

    def copy_to(self, destdir: Path) -> "SCHISMGrid":
        """Copy the grid to a destination directory.

        This method generates all the required grid files in the destination directory
        and returns a new SCHISMGrid instance pointing to these files.

        Parameters
        ----------
        destdir : Path
            Destination directory

        Returns
        -------
        SCHISMGrid
            A new SCHISMGrid instance with sources pointing to the new files
        """
        # Copy grid to destination
        self.get(destdir)

        # Return self for method chaining
        return self

    def get(self, destdir: Path) -> dict:
        from rompy.formatting import ARROW

        ret = {}
        dest_path = (
            Path(destdir) if isinstance(destdir, (str, Path)) else Path(str(destdir))
        )

        # Ensure the output directory exists
        if not dest_path.exists():
            dest_path.mkdir(parents=True, exist_ok=True)

        # Process .gr3 files
        for filetype in G3FILES + ["hgrid"]:
            source = getattr(self, filetype)
            if source is not None:
                ret[filetype] = source.get(destdir, name=f"{filetype}.gr3")

        # Process other grid files, but handle vgrid separately
        for filetype in GRIDLINKS + ["wwmbnd"]:
            source = getattr(self, filetype)
            if source is not None:
                try:
                    ret[filetype] = source.get(destdir)
                except Exception as e:
                    logger.error(f"Error generating {filetype}: {e}")

        # Generate vertical grid
        logger.info(f"{ARROW} Generating vertical grid configuration")
        ret["vgrid"] = self.vgrid.get(destdir)

        # Create symlinks for special grid files
        try:
            hgrid_gr3_path = dest_path / "hgrid.gr3"
            if hgrid_gr3_path.exists():
                # Create symlinks for hgrid_WWM.gr3 and hgrid.ll to hgrid.gr3
                for symlink_name in ["hgrid.ll", "hgrid_WWM.gr3"]:
                    symlink_path = dest_path / symlink_name
                    if not symlink_path.exists():
                        try:
                            # Creating relative symlink
                            symlink_path.symlink_to("hgrid.gr3")
                            logger.info(f"Created symlink {symlink_path} -> hgrid.gr3")
                        except Exception as e:
                            logger.warning(
                                f"Failed to create symlink {symlink_path}: {e}"
                            )
        except Exception as e:
            logger.warning(f"Failed to create grid symlinks: {e}")

        # Generate tvd.prop if needed
        self.generate_tvprop(destdir)
        return ret

    # The _create_gr3_from_hgrid method has been removed as we now use PyLibs' native
    # write_hgrid method to create gr3 files with uniform values

    def generate_tvprop(self, destdir: Path) -> Path:
        """Generate tvd.prop file for SCHISM.

        The tvd.prop file must have two columns in this format:
        1. Two columns: `element_number TVD_flag` (space-separated)
        2. One entry per element
        3. TVD flag value of 1 for all entries (1 = upwind TVD)
        4. Element numbers start from 1

        Correct format:
        ```
        1 1
        2 1
        3 1
        ...
        317 1
        ```

        Args:
            destdir (Path): Destination directory

        Returns:
            Path: Path to tvd.prop file
        """
        dest = destdir / "tvd.prop"

        # For tvd.prop we need the number of elements
        num_elements = self.pylibs_hgrid.ne  # Number of elements

        logger.info(
            f"Creating tvd.prop with two-column format for {num_elements} elements"
        )

        # Create the file with the proper format
        with open(dest, "w") as f:
            # Write element_number and TVD flag (1) for each element
            for i in range(1, num_elements + 1):
                f.write(f"{i} 1\n")

        # Ensure file permissions are correct
        try:
            dest.chmod(0o644)  # User read/write, group/others read
            logger.info(f"Successfully created tvd.prop with {num_elements} elements")
        except Exception as e:
            logger.warning(f"Failed to set permissions on tvd.prop: {e}")

        return dest

    def boundary(self, tolerance=None) -> Polygon:
        gd = self.pylibs_hgrid

        # Make sure boundaries are computed
        if hasattr(gd, "compute_bnd") and not hasattr(gd, "nob"):
            gd.compute_bnd()

        if not hasattr(gd, "nob") or gd.nob is None or gd.nob == 0:
            logger.warning("No open boundaries found in grid")
            # Return an empty polygon
            return Polygon()

        # Extract coordinates for the first open boundary
        boundary_nodes = gd.iobn[0]
        x = gd.x[boundary_nodes]
        y = gd.y[boundary_nodes]

        # Create a polygon
        polygon = Polygon(zip(x, y))
        if tolerance:
            polygon = polygon.simplify(tolerance=tolerance)
        return polygon

    def plot_bnd(self, ax=None, add_coastlines=True, **kwargs):
        # Make sure boundaries are computed if needed
        if hasattr(self.pylibs_hgrid, "compute_bnd") and not hasattr(
            self.pylibs_hgrid, "nob"
        ):
            self.pylibs_hgrid.compute_bnd()

        # Use the native pylibs plotting function
        return self.plot(fmt=3, **kwargs)

    def plot_grid(self, ax=None, add_coastlines=True, **kwargs):
        """
        Plot just the grid triangulation.

        Parameters
        ----------
        ax : matplotlib.axes.Axes, optional
            The axes to plot on. If None, a new figure is created.
        add_coastlines : bool, optional:w

            Whether to add coastlines to the plot (requires cartopy).
        **kwargs : dict
            Additional keyword arguments to pass to the pylibs plot functions.

        Returns
        -------
        fig : matplotlib.figure.Figure
            The figure object.
        ax : matplotlib.axes.Axes
            The axes object.
        """
        return self.plot(fmt=0, **kwargs)

    def plot_bathymetry(self, ax=None, add_coastlines=True, **kwargs):
        """
        Plot filled contours of depth/bathymetry.

        Parameters
        ----------
        ax : matplotlib.axes.Axes, optional
            The axes to plot on. If None, a new figure is created.
        add_coastlines : bool, optional
            Whether to add coastlines to the plot (requires cartopy).
        **kwargs : dict
            Additional keyword arguments to pass to the pylibs plot functions.

        Returns
        -------
        fig : matplotlib.figure.Figure
            The figure object.
        ax : matplotlib.axes.Axes
            The axes object.
        """
        return self.plot(fmt=1, **kwargs)

    def plot_contours(self, ax=None, add_coastlines=True, **kwargs):
        """
        Plot contour lines of depth/bathymetry.

        Parameters
        ----------
        ax : matplotlib.axes.Axes, optional
            The axes to plot on. If None, a new figure is created.
        add_coastlines : bool, optional
            Whether to add coastlines to the plot (requires cartopy).
        **kwargs : dict
            Additional keyword arguments to pass to the pylibs plot functions.

        Returns
        -------
        fig : matplotlib.figure.Figure
            The figure object.
        ax : matplotlib.axes.Axes
            The axes object.
        """
        return self.plot(fmt=2, **kwargs)

    def plot(self, ax=None, plot_type="domain", add_coastlines=True, **kwargs):
        """
        Plot the SCHISM grid using native pylibs plotting functionality.

        Parameters
        ----------
        ax : matplotlib.axes.Axes, optional
            The axes to plot on. If None, a new figure is created.
        plot_type : str, optional
            Type of plot to create. Options are:
            - 'domain': Plot the full domain with boundaries (default)
            - 'grid': Plot just the grid triangulation
            - 'bnd': Plot just the boundaries
        add_coastlines : bool, optional
            Whether to add coastlines to the plot (requires cartopy).
        fmt : int, optional
            Plotting format. Options are:
            - 0: Plot grid only (default)
            - 1: Plot filled contours of depth/bathymetry
            - 2: Plot contour lines of depth/bathymetry
            - 3: Plot boundaries only
        value : numpy.ndarray, optional
            Color values for plotting. If None, grid depth is used.
        levels : int or array-like, optional
            If int, number of contour levels to use (default 51).
            If array-like, specific levels to plot.
        clim : [min, max], optional
            Value range for plot/colorbar. If None, determined from data.
        cmap : str, optional
            Colormap to use for depth visualization (default 'jet').
        cb : bool, optional
            Whether to add a colorbar to the plot (default True).
        **kwargs : dict
            Additional keyword arguments to pass to the pylibs plot functions.

        Returns
        -------
        fig : matplotlib.figure.Figure
            The figure object.
        ax : matplotlib.axes.Axes
            The axes object.

        Examples
        --------
        # Plot grid with bathymetry as filled contours
        >>> grid.plot(fmt=1, cmap='viridis', levels=20)

        # Plot specific depth contours with custom range
        >>> grid.plot(fmt=2, clim=[-100, 0], levels=[-100, -50, -20, -10, 0])
        """
        import matplotlib.pyplot as plt

        if ax is None:
            fig = plt.figure(figsize=(12, 10))
            try:
                # Try to create a cartopy axis if available
                from cartopy import crs as ccrs

                ax = fig.add_subplot(111, projection=ccrs.PlateCarree())
                if add_coastlines:
                    ax.coastlines()
                    ax.gridlines(draw_labels=True)
            except ImportError:
                # Fall back to regular axis if cartopy isn't available
                ax = fig.add_subplot(111)
        else:
            fig = plt.gcf()
        self.pylibs_hgrid.plot(**kwargs)
        self.pylibs_hgrid.plot_bnd()
        return fig, ax

    def plot_hgrid(self, figsize=(20, 10)):
        """
        Create a comprehensive two-panel visualization of the SCHISM grid.

        Left panel shows bathymetry/depth, right panel shows the mesh and boundaries.

        Parameters
        ----------
        figsize : tuple, optional
            Size of the figure (width, height). Default is (20, 10).

        Returns
        -------
        fig : matplotlib.figure.Figure
            The figure object containing both panels.
        (ax1, ax2) : tuple of matplotlib.axes.Axes
            The axes objects for the bathymetry and mesh panels.
        """
        import matplotlib.pyplot as plt
        from matplotlib.tri import Triangulation

        # Create figure with two subplots
        fig = plt.figure(figsize=figsize)

        # Left panel: Bathymetry
        try:
            from cartopy import crs as ccrs

            ax1 = fig.add_subplot(121, projection=ccrs.PlateCarree())
            ax1.coastlines()
        except ImportError:
            ax1 = fig.add_subplot(121)

        # Plot bathymetry using pylibs
        gd = self.pylibs_hgrid
        tri = Triangulation(gd.x, gd.y, triangles=gd.i34)
        depth = -gd.dp  # Convert to positive for depth
        cs = ax1.tricontourf(tri, depth, cmap="viridis")
        plt.colorbar(cs, ax=ax1, label="Depth (m)")
        ax1.set_title("Bathymetry")
        ax1.set_xlabel("Longitude")
        ax1.set_ylabel("Latitude")

        # Right panel: Mesh and boundaries
        try:
            ax2 = fig.add_subplot(122, projection=ccrs.PlateCarree())
            ax2.coastlines()
        except ImportError:
            ax2 = fig.add_subplot(122)

        # Use the main plot method for the mesh
        _, ax2 = self.plot(ax=ax2, plot_type="domain", linewidth=0.5, color="gray")
        ax2.set_title("Grid Mesh and Boundaries")
        ax2.set_xlabel("Longitude")
        ax2.set_ylabel("Latitude")

        plt.tight_layout()
        return fig, (ax1, ax2)

    def ocean_boundary(self):
        gd = self.pylibs_hgrid

        # Make sure boundaries are computed
        if hasattr(gd, "compute_bnd") and not hasattr(gd, "nob"):
            gd.compute_bnd()

        if not hasattr(gd, "nob") or gd.nob is None or gd.nob == 0:
            logger.warning("No open boundaries found in grid")
            return np.array([]), np.array([])

        # Collect all open boundary coordinates
        x_coords = []
        y_coords = []

        for i in range(gd.nob):
            boundary_nodes = gd.iobn[i]
            x_coords.extend(gd.x[boundary_nodes])
            y_coords.extend(gd.y[boundary_nodes])

        return np.array(x_coords), np.array(y_coords)

    def land_boundary(self):
        gd = self.pylibs_hgrid

        # Make sure boundaries are computed
        if hasattr(gd, "compute_bnd") and not hasattr(gd, "nob"):
            gd.compute_bnd()

        if not hasattr(gd, "nlb") or gd.nlb is None or gd.nlb == 0:
            logger.warning("No land boundaries found in grid")
            return np.array([]), np.array([])

        # Collect all land boundary coordinates
        x_coords = []
        y_coords = []

        for i in range(gd.nlb):
            boundary_nodes = gd.ilbn[i]
            x_coords.extend(gd.x[boundary_nodes])
            y_coords.extend(gd.y[boundary_nodes])

        return np.array(x_coords), np.array(y_coords)

    def boundary_points(self, spacing=None) -> tuple:
        return self.ocean_boundary()

    @model_serializer
    def serialize_model(self, **kwargs):
        """Custom serializer to handle optional fields properly during serialization."""
        # Start with all fields that have values
        result = {
            field_name: getattr(self, field_name)
            for field_name in self.model_fields
            if getattr(self, field_name, None) is not None
            or field_name in ["grid_type", "hgrid", "crs"]
        }

        # Remove private attributes that shouldn't be in the serialized output
        for key in list(result.keys()):
            if key.startswith("_"):
                del result[key]

        return result

    def _format_value(self, obj):
        """Custom formatter for SCHISMGrid values.

        This method provides special formatting for specific types used in
        SCHISMGrid such as grid components and data sources.

        Args:
            obj: The object to format

        Returns:
            A formatted string or None to use default formatting
        """
        # Import specific types and formatting utilities
        from rompy.formatting import get_formatted_header_footer
        from rompy.logging import LoggingConfig

        # Get ASCII mode setting from LoggingConfig
        logging_config = LoggingConfig()
        USE_ASCII_ONLY = logging_config.use_ascii

        # Format SCHISMGrid (self-formatting)
        if isinstance(obj, SCHISMGrid):
            header, footer, bullet = get_formatted_header_footer(
                title="SCHISM GRID CONFIGURATION", use_ascii=USE_ASCII_ONLY
            )

            lines = [header]

            # Add horizontal grid information
            if hasattr(obj, "hgrid") and obj.hgrid is not None:
                hgrid_path = str(
                    obj.hgrid.uri if hasattr(obj.hgrid, "uri") else obj.hgrid
                )
                if len(hgrid_path) > 50:
                    hgrid_path = "..." + hgrid_path[-47:]
                lines.append(f"  {bullet} Horizontal Grid: {hgrid_path}")

                # Try to get grid statistics
                try:
                    np_points = obj.np
                    ne_elements = obj.ne
                    lines.append(f"      Points: {np_points}, Elements: {ne_elements}")
                except:
                    pass

            # Add vertical grid information
            if hasattr(obj, "vgrid") and obj.vgrid is not None:
                vgrid_type = type(obj.vgrid).__name__
                lines.append(f"  {bullet} Vertical Grid: {vgrid_type}")

                # Add 2D/3D information
                try:
                    is_3d = obj.is_3d
                    grid_dim = "3D" if is_3d else "2D"
                    lines.append(f"      Dimension: {grid_dim}")

                    if is_3d and hasattr(obj, "nvrt") and obj.nvrt is not None:
                        lines.append(f"      Vertical levels: {obj.nvrt}")
                except:
                    pass

            # Add boundary information
            try:
                nob = obj.nob
                if nob and nob > 0:
                    lines.append(f"  {bullet} Open Boundaries: {nob}")
            except:
                pass

            # Add friction information
            friction_types = []
            if hasattr(obj, "drag") and obj.drag is not None:
                friction_types.append("Drag")
            if hasattr(obj, "rough") and obj.rough is not None:
                friction_types.append("Roughness")
            if hasattr(obj, "manning") and obj.manning is not None:
                friction_types.append("Manning")

            if friction_types:
                lines.append(f"  {bullet} Friction: {', '.join(friction_types)}")

            # Add other grid files
            other_files = []
            for attr in [
                "diffmin",
                "diffmax",
                "albedo",
                "watertype",
                "windrot_geo2proj",
            ]:
                if hasattr(obj, attr) and getattr(obj, attr) is not None:
                    other_files.append(attr)

            if other_files:
                lines.append(f"  {bullet} Additional files: {', '.join(other_files)}")

            # Add coordinate system
            if hasattr(obj, "crs") and obj.crs is not None:
                lines.append(f"  {bullet} CRS: {obj.crs}")

            lines.append(footer)
            return "\n".join(lines)

        # Format GR3Generator
        if isinstance(obj, GR3Generator):
            header, footer, _ = get_formatted_header_footer(
                title="GR3 GENERATOR", use_ascii=USE_ASCII_ONLY
            )

            gr3_type = getattr(obj, "gr3_type", "unknown")
            value = getattr(obj, "value", "unknown")

            return f"{header}\n  Type:  {gr3_type}\n  Value: {value}\n{footer}"

        # Format VgridGenerator
        if isinstance(obj, VgridGenerator):
            header, footer, bullet = get_formatted_header_footer(
                title="VGRID GENERATOR", use_ascii=USE_ASCII_ONLY
            )

            lines = [header]

            if hasattr(obj, "vgrid_type"):
                lines.append(f"  {bullet} Type: {obj.vgrid_type}")

            if hasattr(obj, "nlayer") and obj.nlayer is not None:
                lines.append(f"  {bullet} Layers: {obj.nlayer}")

            lines.append(footer)
            return "\n".join(lines)

        # Use the new formatting framework
        from rompy.formatting import format_value

        return format_value(obj)

Attributes

grid_type class-attribute instance-attribute

grid_type: Literal['schism'] = Field('schism', description='Model descriminator')

hgrid class-attribute instance-attribute

hgrid: DataBlob = Field(..., description='Path to hgrid.gr3 file')

vgrid class-attribute instance-attribute

vgrid: Optional[DataBlob | VgridGenerator | VGrid] = Field(description='Path to vgrid.in file', default_factory=create_2d_vgrid)

drag class-attribute instance-attribute

drag: Optional[DataBlob | float | GR3Generator] = Field(default=None, description='Path to drag.gr3 file')

rough class-attribute instance-attribute

rough: Optional[DataBlob | float | GR3Generator] = Field(default=None, description='Path to rough.gr3 file')

manning class-attribute instance-attribute

manning: Optional[DataBlob | float | GR3Generator] = Field(default=None, description='Path to manning.gr3 file')

hgridll class-attribute instance-attribute

hgridll: Optional[DataBlob | int | GridLinker] = Field(default=None, description='Path to hgrid.ll file', validate_default=True)

diffmin class-attribute instance-attribute

diffmin: Optional[DataBlob | float | GR3Generator] = Field(default=1e-06, description='Path to diffmax.gr3 file or constant value', validate_default=True)

diffmax class-attribute instance-attribute

diffmax: Optional[DataBlob | float | GR3Generator] = Field(default=1.0, description='Path to diffmax.gr3 file or constant value', validate_default=True)

albedo class-attribute instance-attribute

albedo: Optional[DataBlob | float | GR3Generator] = Field(default=0.15, description='Path to albedo.gr3 file or constant value', validate_default=True)

watertype class-attribute instance-attribute

watertype: Optional[DataBlob | int | GR3Generator] = Field(default=1, description='Path to watertype.gr3 file or constant value', validate_default=True)

windrot_geo2proj class-attribute instance-attribute

windrot_geo2proj: Optional[DataBlob | float | GR3Generator] = Field(default=0.0, description='Path to windrot_geo2proj.gr3 file or constant value', validate_default=True)

hgrid_WWM class-attribute instance-attribute

hgrid_WWM: Optional[DataBlob | GridLinker] = Field(default=None, description='Path to hgrid_WWM.gr3 file', validate_default=True)

wwmbnd class-attribute instance-attribute

wwmbnd: Optional[DataBlob | WWMBNDGR3Generator] = Field(default=None, description='Path to wwmbnd.gr3 file', validate_default=True)

crs class-attribute instance-attribute

crs: str = Field('epsg:4326', description='Coordinate reference system')

x property

x: ndarray

y property

y: ndarray

ne property

ne: int

np property

np: int

pylibs_hgrid property

pylibs_hgrid

pylibs_vgrid property

pylibs_vgrid

pyschism_hgrid property

pyschism_hgrid

pyschism_vgrid property

pyschism_vgrid

is_3d property

is_3d

nob property

nob

nobn property

nobn

nvrt property

nvrt

Functions

validate_gr3_fields

validate_gr3_fields()

Custom validator to handle GR3Generator conversion during deserialization.

Source code in rompy_schism/grid.py
@model_validator(mode="after")
def validate_gr3_fields(self):
    """Custom validator to handle GR3Generator conversion during deserialization."""
    # Convert GR3Generator fields that might have been serialized as simple values
    gr3_fields = [
        "drag",
        "diffmin",
        "diffmax",
        "albedo",
        "watertype",
        "windrot_geo2proj",
    ]

    for field in gr3_fields:
        value = getattr(self, field, None)
        if (
            value is not None
            and not isinstance(value, (DataBlob, GR3Generator))
            and isinstance(value, (int, float))
        ):
            # Create a GR3Generator instance instead of using the raw value
            gr3_generator = GR3Generator(
                hgrid=self.hgrid,
                gr3_type=field,
                value=value,
            )
            setattr(self, field, gr3_generator)

    return self

validate_rough_drag_manning

validate_rough_drag_manning(v)
Source code in rompy_schism/grid.py
@model_validator(mode="after")
def validate_rough_drag_manning(cls, v):
    fric_sum = sum([v.rough is not None, v.drag is not None, v.manning is not None])
    if fric_sum > 1:
        raise ValueError("Only one of rough, drag, manning can be set")
    if fric_sum == 0:
        raise ValueError("At least one of rough, drag, manning must be set")
    return v

gr3_source_validator classmethod

gr3_source_validator(v, values)
Source code in rompy_schism/grid.py
@field_validator(*G3FILES)
@classmethod
def gr3_source_validator(cls, v, values):
    if v is not None:
        if not isinstance(v, DataBlob):
            v = GR3Generator(
                hgrid=values.data["hgrid"], gr3_type=values.field_name, value=v
            )
    return v
gridlink_validator(v, values)
Source code in rompy_schism/grid.py
@field_validator(*GRIDLINKS)
@classmethod
def gridlink_validator(cls, v, values):
    if v is None:
        v = GridLinker(hgrid=values.data["hgrid"], gridtype=values.field_name)
    return v

wwmbnd_validator classmethod

wwmbnd_validator(v, values)
Source code in rompy_schism/grid.py
@field_validator("wwmbnd")
@classmethod
def wwmbnd_validator(cls, v, values):
    if v is None:
        v = WWMBNDGR3Generator(hgrid=values.data["hgrid"])
    return v

vgrid_validator classmethod

vgrid_validator(v, values)
Source code in rompy_schism/grid.py
@field_validator("vgrid")
@classmethod
def vgrid_validator(cls, v, values):
    if v is None:
        v = VgridGenerator()
    return v

copy_to

copy_to(destdir: Path) -> SCHISMGrid

Copy the grid to a destination directory.

This method generates all the required grid files in the destination directory and returns a new SCHISMGrid instance pointing to these files.

Parameters

destdir : Path Destination directory

Returns

SCHISMGrid A new SCHISMGrid instance with sources pointing to the new files

Source code in rompy_schism/grid.py
def copy_to(self, destdir: Path) -> "SCHISMGrid":
    """Copy the grid to a destination directory.

    This method generates all the required grid files in the destination directory
    and returns a new SCHISMGrid instance pointing to these files.

    Parameters
    ----------
    destdir : Path
        Destination directory

    Returns
    -------
    SCHISMGrid
        A new SCHISMGrid instance with sources pointing to the new files
    """
    # Copy grid to destination
    self.get(destdir)

    # Return self for method chaining
    return self

get

get(destdir: Path) -> dict
Source code in rompy_schism/grid.py
def get(self, destdir: Path) -> dict:
    from rompy.formatting import ARROW

    ret = {}
    dest_path = (
        Path(destdir) if isinstance(destdir, (str, Path)) else Path(str(destdir))
    )

    # Ensure the output directory exists
    if not dest_path.exists():
        dest_path.mkdir(parents=True, exist_ok=True)

    # Process .gr3 files
    for filetype in G3FILES + ["hgrid"]:
        source = getattr(self, filetype)
        if source is not None:
            ret[filetype] = source.get(destdir, name=f"{filetype}.gr3")

    # Process other grid files, but handle vgrid separately
    for filetype in GRIDLINKS + ["wwmbnd"]:
        source = getattr(self, filetype)
        if source is not None:
            try:
                ret[filetype] = source.get(destdir)
            except Exception as e:
                logger.error(f"Error generating {filetype}: {e}")

    # Generate vertical grid
    logger.info(f"{ARROW} Generating vertical grid configuration")
    ret["vgrid"] = self.vgrid.get(destdir)

    # Create symlinks for special grid files
    try:
        hgrid_gr3_path = dest_path / "hgrid.gr3"
        if hgrid_gr3_path.exists():
            # Create symlinks for hgrid_WWM.gr3 and hgrid.ll to hgrid.gr3
            for symlink_name in ["hgrid.ll", "hgrid_WWM.gr3"]:
                symlink_path = dest_path / symlink_name
                if not symlink_path.exists():
                    try:
                        # Creating relative symlink
                        symlink_path.symlink_to("hgrid.gr3")
                        logger.info(f"Created symlink {symlink_path} -> hgrid.gr3")
                    except Exception as e:
                        logger.warning(
                            f"Failed to create symlink {symlink_path}: {e}"
                        )
    except Exception as e:
        logger.warning(f"Failed to create grid symlinks: {e}")

    # Generate tvd.prop if needed
    self.generate_tvprop(destdir)
    return ret

generate_tvprop

generate_tvprop(destdir: Path) -> Path

Generate tvd.prop file for SCHISM.

The tvd.prop file must have two columns in this format: 1. Two columns: element_number TVD_flag (space-separated) 2. One entry per element 3. TVD flag value of 1 for all entries (1 = upwind TVD) 4. Element numbers start from 1

Correct format:

1 1
2 1
3 1
...
317 1

Parameters:

Name Type Description Default
destdir Path

Destination directory

required

Returns:

Name Type Description
Path Path

Path to tvd.prop file

Source code in rompy_schism/grid.py
def generate_tvprop(self, destdir: Path) -> Path:
    """Generate tvd.prop file for SCHISM.

    The tvd.prop file must have two columns in this format:
    1. Two columns: `element_number TVD_flag` (space-separated)
    2. One entry per element
    3. TVD flag value of 1 for all entries (1 = upwind TVD)
    4. Element numbers start from 1

    Correct format:
    ```
    1 1
    2 1
    3 1
    ...
    317 1
    ```

    Args:
        destdir (Path): Destination directory

    Returns:
        Path: Path to tvd.prop file
    """
    dest = destdir / "tvd.prop"

    # For tvd.prop we need the number of elements
    num_elements = self.pylibs_hgrid.ne  # Number of elements

    logger.info(
        f"Creating tvd.prop with two-column format for {num_elements} elements"
    )

    # Create the file with the proper format
    with open(dest, "w") as f:
        # Write element_number and TVD flag (1) for each element
        for i in range(1, num_elements + 1):
            f.write(f"{i} 1\n")

    # Ensure file permissions are correct
    try:
        dest.chmod(0o644)  # User read/write, group/others read
        logger.info(f"Successfully created tvd.prop with {num_elements} elements")
    except Exception as e:
        logger.warning(f"Failed to set permissions on tvd.prop: {e}")

    return dest

boundary

boundary(tolerance=None) -> Polygon
Source code in rompy_schism/grid.py
def boundary(self, tolerance=None) -> Polygon:
    gd = self.pylibs_hgrid

    # Make sure boundaries are computed
    if hasattr(gd, "compute_bnd") and not hasattr(gd, "nob"):
        gd.compute_bnd()

    if not hasattr(gd, "nob") or gd.nob is None or gd.nob == 0:
        logger.warning("No open boundaries found in grid")
        # Return an empty polygon
        return Polygon()

    # Extract coordinates for the first open boundary
    boundary_nodes = gd.iobn[0]
    x = gd.x[boundary_nodes]
    y = gd.y[boundary_nodes]

    # Create a polygon
    polygon = Polygon(zip(x, y))
    if tolerance:
        polygon = polygon.simplify(tolerance=tolerance)
    return polygon

plot_bnd

plot_bnd(ax=None, add_coastlines=True, **kwargs)
Source code in rompy_schism/grid.py
def plot_bnd(self, ax=None, add_coastlines=True, **kwargs):
    # Make sure boundaries are computed if needed
    if hasattr(self.pylibs_hgrid, "compute_bnd") and not hasattr(
        self.pylibs_hgrid, "nob"
    ):
        self.pylibs_hgrid.compute_bnd()

    # Use the native pylibs plotting function
    return self.plot(fmt=3, **kwargs)

plot_grid

plot_grid(ax=None, add_coastlines=True, **kwargs)

Plot just the grid triangulation.

Parameters

ax : matplotlib.axes.Axes, optional The axes to plot on. If None, a new figure is created. add_coastlines : bool, optional:w

Whether to add coastlines to the plot (requires cartopy).

**kwargs : dict Additional keyword arguments to pass to the pylibs plot functions.

Returns

fig : matplotlib.figure.Figure The figure object. ax : matplotlib.axes.Axes The axes object.

Source code in rompy_schism/grid.py
def plot_grid(self, ax=None, add_coastlines=True, **kwargs):
    """
    Plot just the grid triangulation.

    Parameters
    ----------
    ax : matplotlib.axes.Axes, optional
        The axes to plot on. If None, a new figure is created.
    add_coastlines : bool, optional:w

        Whether to add coastlines to the plot (requires cartopy).
    **kwargs : dict
        Additional keyword arguments to pass to the pylibs plot functions.

    Returns
    -------
    fig : matplotlib.figure.Figure
        The figure object.
    ax : matplotlib.axes.Axes
        The axes object.
    """
    return self.plot(fmt=0, **kwargs)

plot_bathymetry

plot_bathymetry(ax=None, add_coastlines=True, **kwargs)

Plot filled contours of depth/bathymetry.

Parameters

ax : matplotlib.axes.Axes, optional The axes to plot on. If None, a new figure is created. add_coastlines : bool, optional Whether to add coastlines to the plot (requires cartopy). **kwargs : dict Additional keyword arguments to pass to the pylibs plot functions.

Returns

fig : matplotlib.figure.Figure The figure object. ax : matplotlib.axes.Axes The axes object.

Source code in rompy_schism/grid.py
def plot_bathymetry(self, ax=None, add_coastlines=True, **kwargs):
    """
    Plot filled contours of depth/bathymetry.

    Parameters
    ----------
    ax : matplotlib.axes.Axes, optional
        The axes to plot on. If None, a new figure is created.
    add_coastlines : bool, optional
        Whether to add coastlines to the plot (requires cartopy).
    **kwargs : dict
        Additional keyword arguments to pass to the pylibs plot functions.

    Returns
    -------
    fig : matplotlib.figure.Figure
        The figure object.
    ax : matplotlib.axes.Axes
        The axes object.
    """
    return self.plot(fmt=1, **kwargs)

plot_contours

plot_contours(ax=None, add_coastlines=True, **kwargs)

Plot contour lines of depth/bathymetry.

Parameters

ax : matplotlib.axes.Axes, optional The axes to plot on. If None, a new figure is created. add_coastlines : bool, optional Whether to add coastlines to the plot (requires cartopy). **kwargs : dict Additional keyword arguments to pass to the pylibs plot functions.

Returns

fig : matplotlib.figure.Figure The figure object. ax : matplotlib.axes.Axes The axes object.

Source code in rompy_schism/grid.py
def plot_contours(self, ax=None, add_coastlines=True, **kwargs):
    """
    Plot contour lines of depth/bathymetry.

    Parameters
    ----------
    ax : matplotlib.axes.Axes, optional
        The axes to plot on. If None, a new figure is created.
    add_coastlines : bool, optional
        Whether to add coastlines to the plot (requires cartopy).
    **kwargs : dict
        Additional keyword arguments to pass to the pylibs plot functions.

    Returns
    -------
    fig : matplotlib.figure.Figure
        The figure object.
    ax : matplotlib.axes.Axes
        The axes object.
    """
    return self.plot(fmt=2, **kwargs)

plot

plot(ax=None, plot_type='domain', add_coastlines=True, **kwargs)

Plot the SCHISM grid using native pylibs plotting functionality.

Parameters

ax : matplotlib.axes.Axes, optional The axes to plot on. If None, a new figure is created. plot_type : str, optional Type of plot to create. Options are: - 'domain': Plot the full domain with boundaries (default) - 'grid': Plot just the grid triangulation - 'bnd': Plot just the boundaries add_coastlines : bool, optional Whether to add coastlines to the plot (requires cartopy). fmt : int, optional Plotting format. Options are: - 0: Plot grid only (default) - 1: Plot filled contours of depth/bathymetry - 2: Plot contour lines of depth/bathymetry - 3: Plot boundaries only value : numpy.ndarray, optional Color values for plotting. If None, grid depth is used. levels : int or array-like, optional If int, number of contour levels to use (default 51). If array-like, specific levels to plot. clim : [min, max], optional Value range for plot/colorbar. If None, determined from data. cmap : str, optional Colormap to use for depth visualization (default 'jet'). cb : bool, optional Whether to add a colorbar to the plot (default True). **kwargs : dict Additional keyword arguments to pass to the pylibs plot functions.

Returns

fig : matplotlib.figure.Figure The figure object. ax : matplotlib.axes.Axes The axes object.

Examples
Plot grid with bathymetry as filled contours

grid.plot(fmt=1, cmap='viridis', levels=20)

Plot specific depth contours with custom range

grid.plot(fmt=2, clim=[-100, 0], levels=[-100, -50, -20, -10, 0])

Source code in rompy_schism/grid.py
def plot(self, ax=None, plot_type="domain", add_coastlines=True, **kwargs):
    """
    Plot the SCHISM grid using native pylibs plotting functionality.

    Parameters
    ----------
    ax : matplotlib.axes.Axes, optional
        The axes to plot on. If None, a new figure is created.
    plot_type : str, optional
        Type of plot to create. Options are:
        - 'domain': Plot the full domain with boundaries (default)
        - 'grid': Plot just the grid triangulation
        - 'bnd': Plot just the boundaries
    add_coastlines : bool, optional
        Whether to add coastlines to the plot (requires cartopy).
    fmt : int, optional
        Plotting format. Options are:
        - 0: Plot grid only (default)
        - 1: Plot filled contours of depth/bathymetry
        - 2: Plot contour lines of depth/bathymetry
        - 3: Plot boundaries only
    value : numpy.ndarray, optional
        Color values for plotting. If None, grid depth is used.
    levels : int or array-like, optional
        If int, number of contour levels to use (default 51).
        If array-like, specific levels to plot.
    clim : [min, max], optional
        Value range for plot/colorbar. If None, determined from data.
    cmap : str, optional
        Colormap to use for depth visualization (default 'jet').
    cb : bool, optional
        Whether to add a colorbar to the plot (default True).
    **kwargs : dict
        Additional keyword arguments to pass to the pylibs plot functions.

    Returns
    -------
    fig : matplotlib.figure.Figure
        The figure object.
    ax : matplotlib.axes.Axes
        The axes object.

    Examples
    --------
    # Plot grid with bathymetry as filled contours
    >>> grid.plot(fmt=1, cmap='viridis', levels=20)

    # Plot specific depth contours with custom range
    >>> grid.plot(fmt=2, clim=[-100, 0], levels=[-100, -50, -20, -10, 0])
    """
    import matplotlib.pyplot as plt

    if ax is None:
        fig = plt.figure(figsize=(12, 10))
        try:
            # Try to create a cartopy axis if available
            from cartopy import crs as ccrs

            ax = fig.add_subplot(111, projection=ccrs.PlateCarree())
            if add_coastlines:
                ax.coastlines()
                ax.gridlines(draw_labels=True)
        except ImportError:
            # Fall back to regular axis if cartopy isn't available
            ax = fig.add_subplot(111)
    else:
        fig = plt.gcf()
    self.pylibs_hgrid.plot(**kwargs)
    self.pylibs_hgrid.plot_bnd()
    return fig, ax

plot_hgrid

plot_hgrid(figsize=(20, 10))

Create a comprehensive two-panel visualization of the SCHISM grid.

Left panel shows bathymetry/depth, right panel shows the mesh and boundaries.

Parameters

figsize : tuple, optional Size of the figure (width, height). Default is (20, 10).

Returns

fig : matplotlib.figure.Figure The figure object containing both panels. (ax1, ax2) : tuple of matplotlib.axes.Axes The axes objects for the bathymetry and mesh panels.

Source code in rompy_schism/grid.py
def plot_hgrid(self, figsize=(20, 10)):
    """
    Create a comprehensive two-panel visualization of the SCHISM grid.

    Left panel shows bathymetry/depth, right panel shows the mesh and boundaries.

    Parameters
    ----------
    figsize : tuple, optional
        Size of the figure (width, height). Default is (20, 10).

    Returns
    -------
    fig : matplotlib.figure.Figure
        The figure object containing both panels.
    (ax1, ax2) : tuple of matplotlib.axes.Axes
        The axes objects for the bathymetry and mesh panels.
    """
    import matplotlib.pyplot as plt
    from matplotlib.tri import Triangulation

    # Create figure with two subplots
    fig = plt.figure(figsize=figsize)

    # Left panel: Bathymetry
    try:
        from cartopy import crs as ccrs

        ax1 = fig.add_subplot(121, projection=ccrs.PlateCarree())
        ax1.coastlines()
    except ImportError:
        ax1 = fig.add_subplot(121)

    # Plot bathymetry using pylibs
    gd = self.pylibs_hgrid
    tri = Triangulation(gd.x, gd.y, triangles=gd.i34)
    depth = -gd.dp  # Convert to positive for depth
    cs = ax1.tricontourf(tri, depth, cmap="viridis")
    plt.colorbar(cs, ax=ax1, label="Depth (m)")
    ax1.set_title("Bathymetry")
    ax1.set_xlabel("Longitude")
    ax1.set_ylabel("Latitude")

    # Right panel: Mesh and boundaries
    try:
        ax2 = fig.add_subplot(122, projection=ccrs.PlateCarree())
        ax2.coastlines()
    except ImportError:
        ax2 = fig.add_subplot(122)

    # Use the main plot method for the mesh
    _, ax2 = self.plot(ax=ax2, plot_type="domain", linewidth=0.5, color="gray")
    ax2.set_title("Grid Mesh and Boundaries")
    ax2.set_xlabel("Longitude")
    ax2.set_ylabel("Latitude")

    plt.tight_layout()
    return fig, (ax1, ax2)

ocean_boundary

ocean_boundary()
Source code in rompy_schism/grid.py
def ocean_boundary(self):
    gd = self.pylibs_hgrid

    # Make sure boundaries are computed
    if hasattr(gd, "compute_bnd") and not hasattr(gd, "nob"):
        gd.compute_bnd()

    if not hasattr(gd, "nob") or gd.nob is None or gd.nob == 0:
        logger.warning("No open boundaries found in grid")
        return np.array([]), np.array([])

    # Collect all open boundary coordinates
    x_coords = []
    y_coords = []

    for i in range(gd.nob):
        boundary_nodes = gd.iobn[i]
        x_coords.extend(gd.x[boundary_nodes])
        y_coords.extend(gd.y[boundary_nodes])

    return np.array(x_coords), np.array(y_coords)

land_boundary

land_boundary()
Source code in rompy_schism/grid.py
def land_boundary(self):
    gd = self.pylibs_hgrid

    # Make sure boundaries are computed
    if hasattr(gd, "compute_bnd") and not hasattr(gd, "nob"):
        gd.compute_bnd()

    if not hasattr(gd, "nlb") or gd.nlb is None or gd.nlb == 0:
        logger.warning("No land boundaries found in grid")
        return np.array([]), np.array([])

    # Collect all land boundary coordinates
    x_coords = []
    y_coords = []

    for i in range(gd.nlb):
        boundary_nodes = gd.ilbn[i]
        x_coords.extend(gd.x[boundary_nodes])
        y_coords.extend(gd.y[boundary_nodes])

    return np.array(x_coords), np.array(y_coords)

boundary_points

boundary_points(spacing=None) -> tuple
Source code in rompy_schism/grid.py
def boundary_points(self, spacing=None) -> tuple:
    return self.ocean_boundary()

serialize_model

serialize_model(**kwargs)

Custom serializer to handle optional fields properly during serialization.

Source code in rompy_schism/grid.py
@model_serializer
def serialize_model(self, **kwargs):
    """Custom serializer to handle optional fields properly during serialization."""
    # Start with all fields that have values
    result = {
        field_name: getattr(self, field_name)
        for field_name in self.model_fields
        if getattr(self, field_name, None) is not None
        or field_name in ["grid_type", "hgrid", "crs"]
    }

    # Remove private attributes that shouldn't be in the serialized output
    for key in list(result.keys()):
        if key.startswith("_"):
            del result[key]

    return result

Data

Supporting objects for SCHISM data files.jects

SfluxSource

Bases: DataGrid

This is a single variable source for and sflux input

Source code in rompy_schism/data.py
class SfluxSource(DataGrid):
    """This is a single variable source for and sflux input"""

    data_type: Literal["sflux"] = Field(
        default="sflux",
        description="Model type discriminator",
    )
    id: str = Field(default="sflux_source", description="id of the source")
    relative_weight: float = Field(
        1.0,
        description="relative weight of the source file if two files are provided",
    )
    max_window_hours: float = Field(
        120.0,
        description="maximum number of hours (offset from start time in each file) in each file of set 1",
    )
    fail_if_missing: bool = Field(
        True, description="Fail if the source file is missing"
    )
    time_buffer: list[int] = Field(
        default=[0, 1],
        description="Number of source data timesteps to buffer the time range if `filter_time` is True",
    )
    # The source field needs special handling
    source: Any = None
    _variable_names = []

    model_config = ConfigDict(arbitrary_types_allowed=True, extra="ignore")

    def __init__(self, **data):
        # Special handling for the DataGrid source field
        # Pydantic v2 is strict about union tag validation, so we need to handle it manually
        source_obj = None
        if "source" in data:
            source_obj = data.pop("source")  # Remove source to avoid validation errors

        # Initialize without the source field
        try:
            super().__init__(**data)
            # Set the source object after initialization
            if source_obj is not None:
                self.source = source_obj
        except Exception as e:
            logger.error(f"Error initializing SfluxSource: {e}")
            logger.error(f"Input data: {data}")
            raise

        # Initialize variable names
        self._set_variables()

    @property
    def outfile(self) -> str:
        # TODO - filenumber is. Hardcoded to 1 for now.
        return f"{self.id}.{str(1).rjust(4, '0')}.nc"

    def _set_variables(self) -> None:
        for variable in self._variable_names:
            if getattr(self, variable) is not None:
                self.variables.append(getattr(self, variable))

    @property
    def namelist(self) -> dict:
        # ret = self.model_dump()
        ret = {}
        for key, value in self.model_dump().items():
            if key in ["relative_weight", "max_window_hours", "fail_if_missing"]:
                ret.update({f"{self.id}_{key}": value})
        for varname in self._variable_names:
            var = getattr(self, varname)
            if var is not None:
                ret.update({varname: var})
            else:
                ret.update({varname: varname.replace("_name", "")})
        ret.update({f"{self.id}_file": self.id})
        return ret

    @property
    def ds(self):
        """Return the xarray dataset for this data source."""
        ds = self.source.open(
            variables=self.variables, filters=self.filter, coords=self.coords
        )
        # Define a dictionary for potential renaming
        rename_dict = {self.coords.y: "ny_grid", self.coords.x: "nx_grid"}

        # Construct a valid renaming dictionary
        valid_rename_dict = get_valid_rename_dict(ds, rename_dict)

        # Perform renaming if necessary
        if valid_rename_dict:
            ds = ds.rename_dims(valid_rename_dict)

        lon, lat = np.meshgrid(ds[self.coords.x], ds[self.coords.y])
        ds["lon"] = (("ny_grid", "nx_grid"), lon)
        ds["lat"] = (("ny_grid", "nx_grid"), lat)
        basedate = pd.to_datetime(ds.time.values[0])
        unit = f"days since {basedate.strftime('%Y-%m-%d %H:%M:%S')}"
        ds.time.attrs = {
            "long_name": "Time",
            "standard_name": "time",
            "base_date": np.int32(
                np.array(
                    [
                        basedate.year,
                        basedate.month,
                        basedate.day,
                        basedate.hour,
                        basedate.minute,
                        basedate.second,
                    ]
                )
            ),
            # "units": unit,
        }
        ds.time.encoding["units"] = unit
        ds.time.encoding["calendar"] = "proleptic_gregorian"
        # open bad dataset

        # SCHISM doesn't like scale_factor and add_offset attributes and requires Float64 values
        for var in ds.data_vars:
            # If the variable has scale_factor or add_offset attributes, remove them
            if "scale_factor" in ds[var].encoding:
                del ds[var].encoding["scale_factor"]
            if "add_offset" in ds[var].encoding:
                del ds[var].encoding["add_offset"]
            # set the data variable encoding to Float64
            ds[var].encoding["dtype"] = np.dtypes.Float64DType()

        return ds

Attributes

data_type class-attribute instance-attribute

data_type: Literal['sflux'] = Field(default='sflux', description='Model type discriminator')

id class-attribute instance-attribute

id: str = Field(default='sflux_source', description='id of the source')

relative_weight class-attribute instance-attribute

relative_weight: float = Field(1.0, description='relative weight of the source file if two files are provided')

max_window_hours class-attribute instance-attribute

max_window_hours: float = Field(120.0, description='maximum number of hours (offset from start time in each file) in each file of set 1')

fail_if_missing class-attribute instance-attribute

fail_if_missing: bool = Field(True, description='Fail if the source file is missing')

time_buffer class-attribute instance-attribute

time_buffer: list[int] = Field(default=[0, 1], description='Number of source data timesteps to buffer the time range if `filter_time` is True')

source class-attribute instance-attribute

source: Any = None

model_config class-attribute instance-attribute

model_config = ConfigDict(arbitrary_types_allowed=True, extra='ignore')

outfile property

outfile: str

namelist property

namelist: dict

ds property

ds

Return the xarray dataset for this data source.

SfluxAir

Bases: SfluxSource

This is a single variable source for and sflux input

Source code in rompy_schism/data.py
class SfluxAir(SfluxSource):
    """This is a single variable source for and sflux input"""

    data_type: Literal["sflux_air"] = Field(
        default="sflux_air",
        description="Model type discriminator",
    )
    uwind_name: Optional[str] = Field(
        None,
        description="name of zonal wind variable in source",
    )
    vwind_name: Optional[str] = Field(
        None,
        description="name of meridional wind variable in source",
    )
    prmsl_name: Optional[str] = Field(
        None,
        description="name of mean sea level pressure variable in source",
    )
    stmp_name: Optional[str] = Field(
        None,
        description="name of surface air temperature variable in source",
    )
    spfh_name: Optional[str] = Field(
        None,
        description="name of specific humidity variable in source",
    )

    # Allow extra fields during validation but exclude them from the model
    model_config = ConfigDict(
        arbitrary_types_allowed=True,
        validate_assignment=True,
        extra="allow",  # Allow extra fields during validation
        populate_by_name=True,  # Enable population by field name
    )

    def __init__(self, **data):
        # Initialize logger at the beginning

        # Pre-process parameters before passing to pydantic
        # Map parameters without _name suffix to ones with suffix
        name_mappings = {
            "uwind": "uwind_name",
            "vwind": "vwind_name",
            "prmsl": "prmsl_name",
            "stmp": "stmp_name",
            "spfh": "spfh_name",
        }

        for old_name, new_name in name_mappings.items():
            if old_name in data and new_name not in data:
                data[new_name] = data.pop(old_name)

        # Extract source to handle it separately (avoiding validation problems)
        source_obj = None
        if "source" in data:
            source_obj = data.pop("source")  # Remove source to avoid validation errors

            # Import here to avoid circular import
            from rompy.core.source import SourceFile, SourceIntake

            # If source is a dictionary, convert it to a proper source object
            if isinstance(source_obj, dict):
                logger.info(
                    f"Converting source dictionary to source object: {source_obj}"
                )

                # Handle different source types based on what's in the dictionary
                if "uri" in source_obj:
                    # Create a SourceFile or SourceIntake based on the URI
                    uri = source_obj["uri"]
                    if uri.startswith("intake://") or uri.endswith(".yaml"):
                        source_obj = SourceIntake(uri=uri)
                    else:
                        source_obj = SourceFile(uri=uri)
                    logger.info(f"Created source object from URI: {uri}")
                else:
                    # If no URI, create a minimal valid source
                    logger.warning(
                        "Source dictionary does not contain URI, creating a minimal source"
                    )
                    # Default to a sample data source for testing
                    source_obj = SourceFile(uri="../../tests/data/schism/sample.nc")
        else:
            raise ValueError("SfluxAir requires a 'source' parameter")

        # Call the parent constructor with the processed data (without source)
        try:
            super().__init__(**data)
        except Exception as e:
            logger.error(f"Error initializing SfluxAir: {e}")
            logger.error(f"Input data: {data}")
            raise

        # Set source manually after initialization
        self.source = source_obj
        logger.info(
            f"Successfully created SfluxAir instance with source type: {type(self.source)}"
        )

    _variable_names = [
        "uwind_name",
        "vwind_name",
        "prmsl_name",
        "stmp_name",
        "spfh_name",
    ]

    @property
    def ds(self):
        """Return the xarray dataset for this data source."""
        ds = super().ds
        for variable in self._variable_names:
            data_var = getattr(self, variable)
            if data_var is None:
                proxy_var = variable.replace("_name", "")
                ds[proxy_var] = ds[self.uwind_name].copy()
                if variable == "spfh_name":
                    missing = 0.01
                else:
                    missing = -999
                ds[proxy_var][:, :, :] = missing
                ds.data_vars[proxy_var].attrs["long_name"] = proxy_var
        return ds

Attributes

data_type class-attribute instance-attribute

data_type: Literal['sflux_air'] = Field(default='sflux_air', description='Model type discriminator')

uwind_name class-attribute instance-attribute

uwind_name: Optional[str] = Field(None, description='name of zonal wind variable in source')

vwind_name class-attribute instance-attribute

vwind_name: Optional[str] = Field(None, description='name of meridional wind variable in source')

prmsl_name class-attribute instance-attribute

prmsl_name: Optional[str] = Field(None, description='name of mean sea level pressure variable in source')

stmp_name class-attribute instance-attribute

stmp_name: Optional[str] = Field(None, description='name of surface air temperature variable in source')

spfh_name class-attribute instance-attribute

spfh_name: Optional[str] = Field(None, description='name of specific humidity variable in source')

model_config class-attribute instance-attribute

model_config = ConfigDict(arbitrary_types_allowed=True, validate_assignment=True, extra='allow', populate_by_name=True)

source instance-attribute

source = source_obj

ds property

ds

Return the xarray dataset for this data source.

SfluxRad

Bases: SfluxSource

This is a single variable source for and sflux input

Source code in rompy_schism/data.py
class SfluxRad(SfluxSource):
    """This is a single variable source for and sflux input"""

    data_type: Literal["sflux_rad"] = Field(
        default="sflux_rad",
        description="Model type discriminator",
    )
    dlwrf_name: str = Field(
        None,
        description="name of downward long wave radiation variable in source",
    )
    dswrf_name: str = Field(
        None,
        description="name of downward short wave radiation variable in source",
    )
    _variable_names = ["dlwrf_name", "dswrf_name"]

Attributes

data_type class-attribute instance-attribute

data_type: Literal['sflux_rad'] = Field(default='sflux_rad', description='Model type discriminator')

dlwrf_name class-attribute instance-attribute

dlwrf_name: str = Field(None, description='name of downward long wave radiation variable in source')

dswrf_name class-attribute instance-attribute

dswrf_name: str = Field(None, description='name of downward short wave radiation variable in source')

SfluxPrc

Bases: SfluxSource

This is a single variable source for and sflux input

Source code in rompy_schism/data.py
class SfluxPrc(SfluxSource):
    """This is a single variable source for and sflux input"""

    data_type: Literal["sflux_prc"] = Field(
        default="sflux_rad",
        description="Model type discriminator",
    )
    prate_name: str = Field(
        None,
        description="name of precipitation rate variable in source",
    )
    _variable_names = ["prate_name"]

Attributes

data_type class-attribute instance-attribute

data_type: Literal['sflux_prc'] = Field(default='sflux_rad', description='Model type discriminator')

prate_name class-attribute instance-attribute

prate_name: str = Field(None, description='name of precipitation rate variable in source')

SCHISMDataBoundary

Bases: DataBoundary

This class is used to extract ocean boundary data from a griddd dataset at all open boundary nodes.

Source code in rompy_schism/data.py
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
class SCHISMDataBoundary(DataBoundary):
    """This class is used to extract ocean boundary data from a griddd dataset at all open
    boundary nodes."""

    data_type: Literal["boundary"] = Field(
        default="boundary",
        description="Model type discriminator",
    )
    id: str = Field(
        "bnd",
        description="SCHISM th id of the source",
        json_schema_extra={"choices": ["elev2D", "uv3D", "TEM_3D", "SAL_3D", "bnd"]},
    )

    # This field is used to handle DataGrid sources in Pydantic v2
    data_grid_source: Optional[DataGrid] = Field(
        None, description="DataGrid source for boundary data"
    )
    variables: list[str] = Field(
        default_factory=list, description="variable name in the dataset"
    )
    sel_method: Literal["sel", "interp"] = Field(
        default="interp",
        description=(
            "Xarray method to use for selecting boundary points from the dataset"
        ),
    )
    time_buffer: list[int] = Field(
        default=[0, 1],
        description="Number of source data timesteps to buffer the time range if `filter_time` is True",
    )

    def get(
        self,
        destdir: str | Path,
        grid: SCHISMGrid,
        time: Optional[TimeRange] = None,
    ) -> str:
        """Write the selected boundary data to a netcdf file.
        Parameters
        ----------
        destdir : str | Path
            Destination directory for the netcdf file.
        grid : SCHISMGrid
            Grid instance to use for selecting the boundary points.
        time: TimeRange, optional
            The times to filter the data to, only used if `self.crop_data` is True.

        Returns
        -------
        outfile : Path
            Path to the netcdf file.

        """
        # prepare xarray.Dataset and save forcing netCDF file
        outfile = Path(destdir) / f"{self.id}.th.nc"
        boundary_ds = self.boundary_ds(grid, time)
        boundary_ds.to_netcdf(outfile, "w", "NETCDF3_CLASSIC", unlimited_dims="time")

        # Log file details with dimensions
        if "time_series" in boundary_ds.data_vars:
            shape = boundary_ds.time_series.shape
            logger.debug(f"Saved {self.id} to {outfile} (shape: {shape})")
        else:
            logger.debug(f"Saved boundary data to {outfile}")
        return outfile

    def boundary_ds(self, grid: SCHISMGrid, time: Optional[TimeRange]) -> xr.Dataset:
        """Generate SCHISM boundary dataset from source data.

        This function extracts and formats boundary data for SCHISM from a source dataset.
        For 3D models, it handles vertical interpolation to the SCHISM sigma levels.

        Parameters
        ----------
        grid : SCHISMGrid
            The SCHISM grid to extract boundary data for
        time : Optional[TimeRange]
            The time range to filter data to, if crop_data is True

        Returns
        -------
        xr.Dataset
            Dataset formatted for SCHISM boundary input
        """
        logger.debug(f"Fetching {self.id}")
        if self.crop_data and time is not None:
            self._filter_time(time)

        # Extract boundary data from source
        ds = self._sel_boundary(grid)

        # Calculate time step
        if len(ds.time) > 1:
            dt = total_seconds((ds.time[1] - ds.time[0]).values)
        else:
            dt = 3600

        # Get the variable data - handle multiple variables (e.g., u,v for velocity)
        num_components = len(self.variables)

        # Process all variables and stack them
        variable_data = []
        for var in self.variables:
            variable_data.append(ds[var].values)

        # Stack variables along a new component axis (last axis)
        if num_components == 1:
            data = variable_data[0]
        else:
            data = np.stack(variable_data, axis=-1)

        # Determine if we're working with 3D data
        is_3d_data = grid.is_3d and self.coords.z is not None

        # Handle different data dimensions based on 2D or 3D
        if is_3d_data:
            # Try to determine the dimension order
            if hasattr(ds[self.variables[0]], "dims"):
                # Get dimension names
                dims = list(ds[self.variables[0]].dims)

                # Find indices of time, z, and x dimensions
                time_dim_idx = dims.index(ds.time.dims[0])
                z_dim_idx = (
                    dims.index(ds[self.coords.z].dims[0])
                    if self.coords and self.coords.z and self.coords.z in ds
                    else 1
                )
                x_dim_idx = (
                    dims.index(ds[self.coords.x].dims[0])
                    if self.coords and self.coords.x and self.coords.x in ds
                    else 2
                )

                logger.debug(
                    f"Dimension order: time={time_dim_idx}, z={z_dim_idx}, x={x_dim_idx}"
                )

                # Reshape data to expected format if needed (time, x, z, [components])
                if num_components == 1:
                    # Single component case - need to transpose to (time, x, z)
                    if not (time_dim_idx == 0 and x_dim_idx == 1 and z_dim_idx == 2):
                        trans_dims = list(range(data.ndim))
                        trans_dims[time_dim_idx] = 0
                        trans_dims[x_dim_idx] = 1
                        trans_dims[z_dim_idx] = 2

                        data = np.transpose(data, trans_dims)
                        logger.debug(f"Transposed data shape: {data.shape}")

                    # Add the component dimension for SCHISM
                    time_series = np.expand_dims(data, axis=3)
                else:
                    # Multiple component case - data is already (time, x, z, components)
                    # Need to transpose the first 3 dimensions to (time, x, z) if needed
                    if not (time_dim_idx == 0 and x_dim_idx == 1 and z_dim_idx == 2):
                        trans_dims = list(
                            range(data.ndim - 1)
                        )  # Exclude component axis
                        trans_dims[time_dim_idx] = 0
                        trans_dims[x_dim_idx] = 1
                        trans_dims[z_dim_idx] = 2
                        # Keep component axis at the end
                        trans_dims.append(data.ndim - 1)

                        data = np.transpose(data, trans_dims)
                        logger.debug(f"Transposed data shape: {data.shape}")

                    # Data already has component dimension from stacking
                    time_series = data
            else:
                # Fallback: add component dimension if needed
                if num_components == 1:
                    time_series = np.expand_dims(data, axis=3)
                else:
                    time_series = data

            # Calculate zcor for 3D
            # For PyLibs vgrid, extract sigma coordinates differently
            gd = grid.pylibs_hgrid
            vgd = grid.pylibs_vgrid

            # Make sure boundaries are computed
            if hasattr(gd, "compute_bnd") and not hasattr(gd, "nob"):
                gd.compute_bnd()

            # Extract boundary information
            if not hasattr(gd, "nob") or gd.nob is None or gd.nob == 0:
                raise ValueError("No open boundary nodes found in the grid")

            # Collect all boundary nodes
            boundary_indices = []
            for i in range(gd.nob):
                boundary_indices.extend(gd.iobn[i])

            # Get bathymetry for boundary nodes
            boundary_depths = gd.dp[boundary_indices]

            # Get sigma levels from vgrid
            # Note: This assumes a simple sigma or SZ grid format
            # For more complex vgrids, more sophisticated extraction would be needed
            if vgd is not None:
                if hasattr(vgd, "sigma"):
                    sigma_levels = vgd.sigma.copy()
                    num_sigma_levels = len(sigma_levels)
                else:
                    # Default sigma levels if not available
                    sigma_levels = np.array([-1.0, 0.0])
                    num_sigma_levels = 2

                # Get fixed z levels if available
                if hasattr(vgd, "ztot"):
                    z_levels = vgd.ztot
                else:
                    z_levels = np.array([])

            # For each boundary point, determine the total number of vertical levels
            # and create appropriate zcor arrays
            all_zcors = []
            all_nvrt = []

            for i, (node_idx, depth) in enumerate(
                zip(boundary_indices, boundary_depths)
            ):
                # Check if we're in deep water (depth > first z level)
                if z_levels.size > 0 and depth > z_levels[0]:
                    # In deep water, find applicable z levels (between first z level and actual depth)
                    first_z_level = z_levels[0]
                    z_mask = (z_levels > first_z_level) & (z_levels < depth)
                    applicable_z = z_levels[z_mask] if np.any(z_mask) else []

                    # Total levels = sigma levels + applicable z levels
                    total_levels = num_sigma_levels + len(applicable_z)

                    # Create zcor for this boundary point
                    node_zcor = np.zeros(total_levels)

                    # First, calculate sigma levels using the first z level as the "floor"
                    for j in range(num_sigma_levels):
                        node_zcor[j] = first_z_level * sigma_levels[j]

                    # Then, add the fixed z levels below the sigma levels
                    for j, z_val in enumerate(applicable_z):
                        node_zcor[num_sigma_levels + j] = z_val

                else:
                    # In shallow water, just use sigma levels scaled to the actual depth
                    total_levels = num_sigma_levels

                    # Create zcor for this boundary point
                    node_zcor = np.zeros(total_levels)

                    for j in range(total_levels):
                        node_zcor[j] = depth * sigma_levels[j]

                # Store this boundary point's zcor and number of levels
                all_zcors.append(node_zcor)
                all_nvrt.append(total_levels)

            # Now we have a list of zcor arrays with potentially different lengths
            # Find the maximum number of levels across all boundary points
            max_nvrt = max(all_nvrt) if all_nvrt else num_sigma_levels

            # Create a uniform zcor array with the maximum number of levels
            zcor = np.zeros((len(boundary_indices), max_nvrt))

            # Fill in the values, leaving zeros for levels beyond a particular boundary point's total
            for i, (node_zcor, nvrt_i) in enumerate(zip(all_zcors, all_nvrt)):
                zcor[i, :nvrt_i] = node_zcor

            # Get source z-levels and prepare for interpolation
            sigma_values = (
                ds[self.coords.z].values
                if self.coords and self.coords.z
                else np.array([0])
            )
            data_shape = time_series.shape

            # Initialize interpolated data array with the maximum number of vertical levels
            if num_components == 1:
                interpolated_data = np.zeros((data_shape[0], data_shape[1], max_nvrt))
            else:
                interpolated_data = np.zeros(
                    (data_shape[0], data_shape[1], max_nvrt, data_shape[3])
                )

            # For each time step and boundary point
            for t in range(data_shape[0]):  # time
                for n in range(data_shape[1]):  # boundary points
                    # Get z-coordinates for this point
                    z_dest = zcor[n, :]
                    nvrt_n = all_nvrt[
                        n
                    ]  # Get the number of vertical levels for this point

                    if num_components == 1:
                        # Extract vertical profile for single component
                        profile = time_series[t, n, :, 0]

                        # Create interpolator for this profile
                        interp = sp.interpolate.interp1d(
                            sigma_values,
                            profile,
                            kind="linear",
                            bounds_error=False,
                            fill_value="extrapolate",
                        )

                        # Interpolate to SCHISM levels for this boundary point
                        # Only interpolate up to the actual number of levels for this point
                        interpolated_data[t, n, :nvrt_n] = interp(z_dest[:nvrt_n])
                    else:
                        # Handle multiple components (e.g., u,v for velocity)
                        for c in range(num_components):
                            # Extract vertical profile for this component
                            profile = time_series[t, n, :, c]

                            # Create interpolator for this profile
                            interp = sp.interpolate.interp1d(
                                sigma_values,
                                profile,
                                kind="linear",
                                bounds_error=False,
                                fill_value="extrapolate",
                            )

                            # Interpolate to SCHISM levels for this boundary point
                            # Only interpolate up to the actual number of levels for this point
                            interpolated_data[t, n, :nvrt_n, c] = interp(
                                z_dest[:nvrt_n]
                            )

            # Replace data with interpolated values
            data = interpolated_data
            if num_components == 1:
                time_series = np.expand_dims(data, axis=3)
            else:
                time_series = data

            # Store the variable vertical levels in the output dataset
            # Create a 2D array where each row contains the vertical levels for a boundary node
            # For nodes with fewer levels, pad with NaN
            vert_levels = np.full((len(boundary_indices), max_nvrt), np.nan)
            for i, (node_zcor, nvrt_i) in enumerate(zip(all_zcors, all_nvrt)):
                vert_levels[i, :nvrt_i] = node_zcor

            # Create output dataset
            schism_ds = xr.Dataset(
                coords={
                    "time": ds.time,
                    "nOpenBndNodes": np.arange(time_series.shape[1]),
                    "nLevels": np.arange(max_nvrt),
                    "nComponents": np.arange(num_components),
                    "one": np.array([1]),
                },
                data_vars={
                    "time_step": (("one"), np.array([dt])),
                    "time_series": (
                        ("time", "nOpenBndNodes", "nLevels", "nComponents"),
                        time_series,
                    ),
                    "vertical_levels": (
                        ("nOpenBndNodes", "nLevels"),
                        vert_levels,
                    ),
                    "num_levels": (
                        ("nOpenBndNodes"),
                        np.array(all_nvrt),
                    ),
                },
            )
        else:
            # # 2D case - simpler handling

            # Add level and component dimensions for SCHISM
            if num_components == 1:
                time_series = np.expand_dims(data, axis=(2, 3))
            else:
                # Multiple components: add level dimension but keep component dimension
                time_series = np.expand_dims(data, axis=2)

            # Create output dataset
            schism_ds = xr.Dataset(
                coords={
                    "time": ds.time,
                    "nOpenBndNodes": np.arange(time_series.shape[1]),
                    "nLevels": np.array([0]),  # Single level for 2D
                    "nComponents": np.arange(num_components),
                    "one": np.array([1]),
                },
                data_vars={
                    "time_step": (("one"), np.array([dt])),
                    "time_series": (
                        ("time", "nOpenBndNodes", "nLevels", "nComponents"),
                        time_series,
                    ),
                },
            )

        # Set attributes and encoding
        schism_ds.time_step.assign_attrs({"long_name": "time_step"})
        basedate = pd.to_datetime(ds.time.values[0])
        unit = f"days since {basedate.strftime('%Y-%m-%d %H:%M:%S')}"
        schism_ds.time.attrs = {
            "long_name": "Time",
            "standard_name": "time",
            "base_date": np.int32(
                np.array(
                    [
                        basedate.year,
                        basedate.month,
                        basedate.day,
                        basedate.hour,
                        basedate.minute,
                        basedate.second,
                    ]
                )
            ),
        }
        schism_ds.time.encoding["units"] = unit
        schism_ds.time.encoding["calendar"] = "proleptic_gregorian"

        # Handle missing values more robustly
        null_count = schism_ds.time_series.isnull().sum().item()
        if null_count > 0:
            logger.debug(
                f"Found {null_count} null values, applying interpolation and filling"
            )

            # Try interpolating along different dimensions
            for dim in ["nOpenBndNodes", "time", "nLevels"]:
                if dim in schism_ds.dims and len(schism_ds[dim]) > 1:
                    schism_ds["time_series"] = schism_ds.time_series.interpolate_na(
                        dim=dim
                    )
                    if not schism_ds.time_series.isnull().any():
                        logger.debug(
                            f"Interpolated missing values along {dim} dimension"
                        )
                        break

            # If still have NaNs, use more aggressive filling methods
            if schism_ds.time_series.isnull().any():
                # Find a reasonable fill value (median of non-NaN values)
                valid_values = schism_ds.time_series.values[
                    ~np.isnan(schism_ds.time_series.values)
                ]
                fill_value = np.median(valid_values) if len(valid_values) > 0 else 0.0
                schism_ds["time_series"] = schism_ds.time_series.fillna(fill_value)
                logger.debug(f"Filled remaining nulls with constant value {fill_value}")

        # Clean up encoding
        for var in schism_ds.data_vars:
            if "scale_factor" in schism_ds[var].encoding:
                del schism_ds[var].encoding["scale_factor"]
            if "add_offset" in schism_ds[var].encoding:
                del schism_ds[var].encoding["add_offset"]
            schism_ds[var].encoding["dtype"] = np.dtypes.Float64DType()

        return schism_ds

Attributes

data_type class-attribute instance-attribute

data_type: Literal['boundary'] = Field(default='boundary', description='Model type discriminator')

id class-attribute instance-attribute

id: str = Field('bnd', description='SCHISM th id of the source', json_schema_extra={'choices': ['elev2D', 'uv3D', 'TEM_3D', 'SAL_3D', 'bnd']})

data_grid_source class-attribute instance-attribute

data_grid_source: Optional[DataGrid] = Field(None, description='DataGrid source for boundary data')

variables class-attribute instance-attribute

variables: list[str] = Field(default_factory=list, description='variable name in the dataset')

sel_method class-attribute instance-attribute

sel_method: Literal['sel', 'interp'] = Field(default='interp', description='Xarray method to use for selecting boundary points from the dataset')

time_buffer class-attribute instance-attribute

time_buffer: list[int] = Field(default=[0, 1], description='Number of source data timesteps to buffer the time range if `filter_time` is True')

Functions

get

get(destdir: str | Path, grid: SCHISMGrid, time: Optional[TimeRange] = None) -> str

Write the selected boundary data to a netcdf file. Parameters


destdir : str | Path Destination directory for the netcdf file. grid : SCHISMGrid Grid instance to use for selecting the boundary points. time: TimeRange, optional The times to filter the data to, only used if self.crop_data is True.

Returns

outfile : Path Path to the netcdf file.

Source code in rompy_schism/data.py
def get(
    self,
    destdir: str | Path,
    grid: SCHISMGrid,
    time: Optional[TimeRange] = None,
) -> str:
    """Write the selected boundary data to a netcdf file.
    Parameters
    ----------
    destdir : str | Path
        Destination directory for the netcdf file.
    grid : SCHISMGrid
        Grid instance to use for selecting the boundary points.
    time: TimeRange, optional
        The times to filter the data to, only used if `self.crop_data` is True.

    Returns
    -------
    outfile : Path
        Path to the netcdf file.

    """
    # prepare xarray.Dataset and save forcing netCDF file
    outfile = Path(destdir) / f"{self.id}.th.nc"
    boundary_ds = self.boundary_ds(grid, time)
    boundary_ds.to_netcdf(outfile, "w", "NETCDF3_CLASSIC", unlimited_dims="time")

    # Log file details with dimensions
    if "time_series" in boundary_ds.data_vars:
        shape = boundary_ds.time_series.shape
        logger.debug(f"Saved {self.id} to {outfile} (shape: {shape})")
    else:
        logger.debug(f"Saved boundary data to {outfile}")
    return outfile

boundary_ds

boundary_ds(grid: SCHISMGrid, time: Optional[TimeRange]) -> Dataset

Generate SCHISM boundary dataset from source data.

This function extracts and formats boundary data for SCHISM from a source dataset. For 3D models, it handles vertical interpolation to the SCHISM sigma levels.

Parameters

grid : SCHISMGrid The SCHISM grid to extract boundary data for time : Optional[TimeRange] The time range to filter data to, if crop_data is True

Returns

xr.Dataset Dataset formatted for SCHISM boundary input

Source code in rompy_schism/data.py
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
def boundary_ds(self, grid: SCHISMGrid, time: Optional[TimeRange]) -> xr.Dataset:
    """Generate SCHISM boundary dataset from source data.

    This function extracts and formats boundary data for SCHISM from a source dataset.
    For 3D models, it handles vertical interpolation to the SCHISM sigma levels.

    Parameters
    ----------
    grid : SCHISMGrid
        The SCHISM grid to extract boundary data for
    time : Optional[TimeRange]
        The time range to filter data to, if crop_data is True

    Returns
    -------
    xr.Dataset
        Dataset formatted for SCHISM boundary input
    """
    logger.debug(f"Fetching {self.id}")
    if self.crop_data and time is not None:
        self._filter_time(time)

    # Extract boundary data from source
    ds = self._sel_boundary(grid)

    # Calculate time step
    if len(ds.time) > 1:
        dt = total_seconds((ds.time[1] - ds.time[0]).values)
    else:
        dt = 3600

    # Get the variable data - handle multiple variables (e.g., u,v for velocity)
    num_components = len(self.variables)

    # Process all variables and stack them
    variable_data = []
    for var in self.variables:
        variable_data.append(ds[var].values)

    # Stack variables along a new component axis (last axis)
    if num_components == 1:
        data = variable_data[0]
    else:
        data = np.stack(variable_data, axis=-1)

    # Determine if we're working with 3D data
    is_3d_data = grid.is_3d and self.coords.z is not None

    # Handle different data dimensions based on 2D or 3D
    if is_3d_data:
        # Try to determine the dimension order
        if hasattr(ds[self.variables[0]], "dims"):
            # Get dimension names
            dims = list(ds[self.variables[0]].dims)

            # Find indices of time, z, and x dimensions
            time_dim_idx = dims.index(ds.time.dims[0])
            z_dim_idx = (
                dims.index(ds[self.coords.z].dims[0])
                if self.coords and self.coords.z and self.coords.z in ds
                else 1
            )
            x_dim_idx = (
                dims.index(ds[self.coords.x].dims[0])
                if self.coords and self.coords.x and self.coords.x in ds
                else 2
            )

            logger.debug(
                f"Dimension order: time={time_dim_idx}, z={z_dim_idx}, x={x_dim_idx}"
            )

            # Reshape data to expected format if needed (time, x, z, [components])
            if num_components == 1:
                # Single component case - need to transpose to (time, x, z)
                if not (time_dim_idx == 0 and x_dim_idx == 1 and z_dim_idx == 2):
                    trans_dims = list(range(data.ndim))
                    trans_dims[time_dim_idx] = 0
                    trans_dims[x_dim_idx] = 1
                    trans_dims[z_dim_idx] = 2

                    data = np.transpose(data, trans_dims)
                    logger.debug(f"Transposed data shape: {data.shape}")

                # Add the component dimension for SCHISM
                time_series = np.expand_dims(data, axis=3)
            else:
                # Multiple component case - data is already (time, x, z, components)
                # Need to transpose the first 3 dimensions to (time, x, z) if needed
                if not (time_dim_idx == 0 and x_dim_idx == 1 and z_dim_idx == 2):
                    trans_dims = list(
                        range(data.ndim - 1)
                    )  # Exclude component axis
                    trans_dims[time_dim_idx] = 0
                    trans_dims[x_dim_idx] = 1
                    trans_dims[z_dim_idx] = 2
                    # Keep component axis at the end
                    trans_dims.append(data.ndim - 1)

                    data = np.transpose(data, trans_dims)
                    logger.debug(f"Transposed data shape: {data.shape}")

                # Data already has component dimension from stacking
                time_series = data
        else:
            # Fallback: add component dimension if needed
            if num_components == 1:
                time_series = np.expand_dims(data, axis=3)
            else:
                time_series = data

        # Calculate zcor for 3D
        # For PyLibs vgrid, extract sigma coordinates differently
        gd = grid.pylibs_hgrid
        vgd = grid.pylibs_vgrid

        # Make sure boundaries are computed
        if hasattr(gd, "compute_bnd") and not hasattr(gd, "nob"):
            gd.compute_bnd()

        # Extract boundary information
        if not hasattr(gd, "nob") or gd.nob is None or gd.nob == 0:
            raise ValueError("No open boundary nodes found in the grid")

        # Collect all boundary nodes
        boundary_indices = []
        for i in range(gd.nob):
            boundary_indices.extend(gd.iobn[i])

        # Get bathymetry for boundary nodes
        boundary_depths = gd.dp[boundary_indices]

        # Get sigma levels from vgrid
        # Note: This assumes a simple sigma or SZ grid format
        # For more complex vgrids, more sophisticated extraction would be needed
        if vgd is not None:
            if hasattr(vgd, "sigma"):
                sigma_levels = vgd.sigma.copy()
                num_sigma_levels = len(sigma_levels)
            else:
                # Default sigma levels if not available
                sigma_levels = np.array([-1.0, 0.0])
                num_sigma_levels = 2

            # Get fixed z levels if available
            if hasattr(vgd, "ztot"):
                z_levels = vgd.ztot
            else:
                z_levels = np.array([])

        # For each boundary point, determine the total number of vertical levels
        # and create appropriate zcor arrays
        all_zcors = []
        all_nvrt = []

        for i, (node_idx, depth) in enumerate(
            zip(boundary_indices, boundary_depths)
        ):
            # Check if we're in deep water (depth > first z level)
            if z_levels.size > 0 and depth > z_levels[0]:
                # In deep water, find applicable z levels (between first z level and actual depth)
                first_z_level = z_levels[0]
                z_mask = (z_levels > first_z_level) & (z_levels < depth)
                applicable_z = z_levels[z_mask] if np.any(z_mask) else []

                # Total levels = sigma levels + applicable z levels
                total_levels = num_sigma_levels + len(applicable_z)

                # Create zcor for this boundary point
                node_zcor = np.zeros(total_levels)

                # First, calculate sigma levels using the first z level as the "floor"
                for j in range(num_sigma_levels):
                    node_zcor[j] = first_z_level * sigma_levels[j]

                # Then, add the fixed z levels below the sigma levels
                for j, z_val in enumerate(applicable_z):
                    node_zcor[num_sigma_levels + j] = z_val

            else:
                # In shallow water, just use sigma levels scaled to the actual depth
                total_levels = num_sigma_levels

                # Create zcor for this boundary point
                node_zcor = np.zeros(total_levels)

                for j in range(total_levels):
                    node_zcor[j] = depth * sigma_levels[j]

            # Store this boundary point's zcor and number of levels
            all_zcors.append(node_zcor)
            all_nvrt.append(total_levels)

        # Now we have a list of zcor arrays with potentially different lengths
        # Find the maximum number of levels across all boundary points
        max_nvrt = max(all_nvrt) if all_nvrt else num_sigma_levels

        # Create a uniform zcor array with the maximum number of levels
        zcor = np.zeros((len(boundary_indices), max_nvrt))

        # Fill in the values, leaving zeros for levels beyond a particular boundary point's total
        for i, (node_zcor, nvrt_i) in enumerate(zip(all_zcors, all_nvrt)):
            zcor[i, :nvrt_i] = node_zcor

        # Get source z-levels and prepare for interpolation
        sigma_values = (
            ds[self.coords.z].values
            if self.coords and self.coords.z
            else np.array([0])
        )
        data_shape = time_series.shape

        # Initialize interpolated data array with the maximum number of vertical levels
        if num_components == 1:
            interpolated_data = np.zeros((data_shape[0], data_shape[1], max_nvrt))
        else:
            interpolated_data = np.zeros(
                (data_shape[0], data_shape[1], max_nvrt, data_shape[3])
            )

        # For each time step and boundary point
        for t in range(data_shape[0]):  # time
            for n in range(data_shape[1]):  # boundary points
                # Get z-coordinates for this point
                z_dest = zcor[n, :]
                nvrt_n = all_nvrt[
                    n
                ]  # Get the number of vertical levels for this point

                if num_components == 1:
                    # Extract vertical profile for single component
                    profile = time_series[t, n, :, 0]

                    # Create interpolator for this profile
                    interp = sp.interpolate.interp1d(
                        sigma_values,
                        profile,
                        kind="linear",
                        bounds_error=False,
                        fill_value="extrapolate",
                    )

                    # Interpolate to SCHISM levels for this boundary point
                    # Only interpolate up to the actual number of levels for this point
                    interpolated_data[t, n, :nvrt_n] = interp(z_dest[:nvrt_n])
                else:
                    # Handle multiple components (e.g., u,v for velocity)
                    for c in range(num_components):
                        # Extract vertical profile for this component
                        profile = time_series[t, n, :, c]

                        # Create interpolator for this profile
                        interp = sp.interpolate.interp1d(
                            sigma_values,
                            profile,
                            kind="linear",
                            bounds_error=False,
                            fill_value="extrapolate",
                        )

                        # Interpolate to SCHISM levels for this boundary point
                        # Only interpolate up to the actual number of levels for this point
                        interpolated_data[t, n, :nvrt_n, c] = interp(
                            z_dest[:nvrt_n]
                        )

        # Replace data with interpolated values
        data = interpolated_data
        if num_components == 1:
            time_series = np.expand_dims(data, axis=3)
        else:
            time_series = data

        # Store the variable vertical levels in the output dataset
        # Create a 2D array where each row contains the vertical levels for a boundary node
        # For nodes with fewer levels, pad with NaN
        vert_levels = np.full((len(boundary_indices), max_nvrt), np.nan)
        for i, (node_zcor, nvrt_i) in enumerate(zip(all_zcors, all_nvrt)):
            vert_levels[i, :nvrt_i] = node_zcor

        # Create output dataset
        schism_ds = xr.Dataset(
            coords={
                "time": ds.time,
                "nOpenBndNodes": np.arange(time_series.shape[1]),
                "nLevels": np.arange(max_nvrt),
                "nComponents": np.arange(num_components),
                "one": np.array([1]),
            },
            data_vars={
                "time_step": (("one"), np.array([dt])),
                "time_series": (
                    ("time", "nOpenBndNodes", "nLevels", "nComponents"),
                    time_series,
                ),
                "vertical_levels": (
                    ("nOpenBndNodes", "nLevels"),
                    vert_levels,
                ),
                "num_levels": (
                    ("nOpenBndNodes"),
                    np.array(all_nvrt),
                ),
            },
        )
    else:
        # # 2D case - simpler handling

        # Add level and component dimensions for SCHISM
        if num_components == 1:
            time_series = np.expand_dims(data, axis=(2, 3))
        else:
            # Multiple components: add level dimension but keep component dimension
            time_series = np.expand_dims(data, axis=2)

        # Create output dataset
        schism_ds = xr.Dataset(
            coords={
                "time": ds.time,
                "nOpenBndNodes": np.arange(time_series.shape[1]),
                "nLevels": np.array([0]),  # Single level for 2D
                "nComponents": np.arange(num_components),
                "one": np.array([1]),
            },
            data_vars={
                "time_step": (("one"), np.array([dt])),
                "time_series": (
                    ("time", "nOpenBndNodes", "nLevels", "nComponents"),
                    time_series,
                ),
            },
        )

    # Set attributes and encoding
    schism_ds.time_step.assign_attrs({"long_name": "time_step"})
    basedate = pd.to_datetime(ds.time.values[0])
    unit = f"days since {basedate.strftime('%Y-%m-%d %H:%M:%S')}"
    schism_ds.time.attrs = {
        "long_name": "Time",
        "standard_name": "time",
        "base_date": np.int32(
            np.array(
                [
                    basedate.year,
                    basedate.month,
                    basedate.day,
                    basedate.hour,
                    basedate.minute,
                    basedate.second,
                ]
            )
        ),
    }
    schism_ds.time.encoding["units"] = unit
    schism_ds.time.encoding["calendar"] = "proleptic_gregorian"

    # Handle missing values more robustly
    null_count = schism_ds.time_series.isnull().sum().item()
    if null_count > 0:
        logger.debug(
            f"Found {null_count} null values, applying interpolation and filling"
        )

        # Try interpolating along different dimensions
        for dim in ["nOpenBndNodes", "time", "nLevels"]:
            if dim in schism_ds.dims and len(schism_ds[dim]) > 1:
                schism_ds["time_series"] = schism_ds.time_series.interpolate_na(
                    dim=dim
                )
                if not schism_ds.time_series.isnull().any():
                    logger.debug(
                        f"Interpolated missing values along {dim} dimension"
                    )
                    break

        # If still have NaNs, use more aggressive filling methods
        if schism_ds.time_series.isnull().any():
            # Find a reasonable fill value (median of non-NaN values)
            valid_values = schism_ds.time_series.values[
                ~np.isnan(schism_ds.time_series.values)
            ]
            fill_value = np.median(valid_values) if len(valid_values) > 0 else 0.0
            schism_ds["time_series"] = schism_ds.time_series.fillna(fill_value)
            logger.debug(f"Filled remaining nulls with constant value {fill_value}")

    # Clean up encoding
    for var in schism_ds.data_vars:
        if "scale_factor" in schism_ds[var].encoding:
            del schism_ds[var].encoding["scale_factor"]
        if "add_offset" in schism_ds[var].encoding:
            del schism_ds[var].encoding["add_offset"]
        schism_ds[var].encoding["dtype"] = np.dtypes.Float64DType()

    return schism_ds

Main objects

SCHISMDataSflux

Bases: RompyBaseModel

Source code in rompy_schism/data.py
class SCHISMDataSflux(RompyBaseModel):
    data_type: Literal["sflux"] = Field(
        default="sflux",
        description="Model type discriminator",
    )
    air_1: Optional[Any] = Field(None, description="sflux air source 1")
    air_2: Optional[Any] = Field(None, description="sflux air source 2")
    rad_1: Optional[Union[DataBlob, SfluxRad]] = Field(
        None, description="sflux rad source 1"
    )
    rad_2: Optional[Union[DataBlob, SfluxRad]] = Field(
        None, description="sflux rad source 2"
    )
    prc_1: Optional[Union[DataBlob, SfluxPrc]] = Field(
        None, description="sflux prc source 1"
    )
    prc_2: Optional[Union[DataBlob, SfluxPrc]] = Field(
        None, description="sflux prc source 2"
    )

    model_config = ConfigDict(arbitrary_types_allowed=True, extra="ignore")

    def __init__(self, **data):
        # Handle 'air' parameter by mapping it to 'air_1'
        if "air" in data:
            air_value = data.pop("air")

            # If air is a dict, convert it to a SfluxAir instance
            if isinstance(air_value, dict):
                try:
                    # Import here to avoid circular import
                    from rompy_schism.data import SfluxAir

                    air_value = SfluxAir(**air_value)
                    logger.info(
                        "Successfully created SfluxAir instance from dictionary"
                    )
                except Exception as e:
                    logger.error(f"Failed to create SfluxAir instance: {e}")
                    # Fall back to passing the dictionary directly
                    logger.info(f"Falling back to dictionary: {air_value}")

            data["air_1"] = air_value

        # Call the parent constructor with the processed data
        super().__init__(**data)

    @model_validator(mode="after")
    def validate_air_fields(self):
        """Validate air fields after model creation."""
        # Convert dictionary to SfluxAir if needed
        if isinstance(self.air_1, dict):
            try:
                # Import here to avoid circular import
                from rompy_schism.data import SfluxAir

                logger.info(
                    f"Converting air_1 dictionary to SfluxAir object: {self.air_1}"
                )
                self.air_1 = SfluxAir(**self.air_1)
                logger.info("Successfully converted air_1 to SfluxAir instance")
            except Exception as e:
                logger.error(f"Error converting air_1 dictionary to SfluxAir: {e}")
                logger.error(f"Input data: {self.air_1}")
                # We'll let validation continue with the dictionary

        if isinstance(self.air_2, dict):
            try:
                from rompy_schism.data import SfluxAir

                logger.info(
                    f"Converting air_2 dictionary to SfluxAir object: {self.air_2}"
                )
                self.air_2 = SfluxAir(**self.air_2)
                logger.info("Successfully converted air_2 to SfluxAir instance")
            except Exception as e:
                logger.error(f"Error converting air_2 dictionary to SfluxAir: {e}")
                logger.error(f"Input data: {self.air_2}")

        return self

    def get(
        self,
        destdir: str | Path,
        grid: Optional[SCHISMGrid] = None,
        time: Optional[TimeRange] = None,
    ) -> Path:
        """Writes SCHISM sflux data from a dataset.

        Args:
            destdir (str | Path): The destination directory to write the sflux data.
            grid (Optional[SCHISMGrid], optional): The grid type. Defaults to None.
            time (Optional[TimeRange], optional): The time range. Defaults to None.

        Returns:
            Path: The path to the written sflux data.

        """
        ret = {}
        destdir = Path(destdir) / "sflux"
        destdir.mkdir(parents=True, exist_ok=True)
        namelistargs = {}

        # Collect information about active variables for logging
        active_variables = []
        source_info = {}

        for variable in ["air_1", "air_2", "rad_1", "rad_2", "prc_1", "prc_2"]:
            data = getattr(self, variable)
            if data is None:
                continue
            data.id = variable
            active_variables.append(variable)

            # Get source information
            if hasattr(data, "source") and hasattr(data.source, "uri"):
                source_info[variable] = str(data.source.uri)

            logger.debug(f"Processing {variable}")
            namelistargs.update(data.namelist)
            # Expand time by one day on each end
            if time is not None:
                time = TimeRange(
                    start=time.start - pd.Timedelta(days=1),
                    end=time.end + pd.Timedelta(days=1),
                )
            ret[variable] = data.get(destdir, grid, time)

        # Log summary of atmospheric data processing
        if active_variables:
            logger.info(f"  • Variables: {', '.join(active_variables)}")
            if source_info:
                unique_sources = list(set(source_info.values()))
                if len(unique_sources) == 1:
                    logger.info(f"  • Source: {unique_sources[0]}")
                else:
                    logger.info(f"  • Sources: {len(unique_sources)} files")
            logger.info(f"  • Output: {destdir}")

        ret["nml"] = Sflux_Inputs(**namelistargs).write_nml(destdir)
        return ret

    @model_validator(mode="after")
    def check_weights(v):
        """Check that relative weights for each pair add to 1.

        Args:
            cls: The class.
            v: The variable.

        Raises:
            ValueError: If the relative weights for any variable do not add up to 1.0.

        """
        for variable in ["air", "rad", "prc"]:
            weight = 0
            active = False
            for i in [1, 2]:
                data = getattr(v, f"{variable}_{i}")
                if data is None:
                    continue
                if data.fail_if_missing:
                    continue
                weight += data.relative_weight
                active = True
            if active and weight != 1.0:
                raise ValueError(
                    f"Relative weights for {variable} do not add to 1.0: {weight}"
                )
            return v
        # SCHISM doesn't like scale_factor and add_offset attributes and requires Float64 values
        for var in ds.data_vars:
            # If the variable has scale_factor or add_offset attributes, remove them
            if "scale_factor" in ds[var].encoding:
                del ds[var].encoding["scale_factor"]
            if "add_offset" in ds[var].encoding:
                del ds[var].encoding["add_offset"]
            # set the data variable encoding to Float64
            ds[var].encoding["dtype"] = np.dtypes.Float64DType()

Attributes

data_type class-attribute instance-attribute

data_type: Literal['sflux'] = Field(default='sflux', description='Model type discriminator')

air_1 class-attribute instance-attribute

air_1: Optional[Any] = Field(None, description='sflux air source 1')

air_2 class-attribute instance-attribute

air_2: Optional[Any] = Field(None, description='sflux air source 2')

rad_1 class-attribute instance-attribute

rad_1: Optional[Union[DataBlob, SfluxRad]] = Field(None, description='sflux rad source 1')

rad_2 class-attribute instance-attribute

rad_2: Optional[Union[DataBlob, SfluxRad]] = Field(None, description='sflux rad source 2')

prc_1 class-attribute instance-attribute

prc_1: Optional[Union[DataBlob, SfluxPrc]] = Field(None, description='sflux prc source 1')

prc_2 class-attribute instance-attribute

prc_2: Optional[Union[DataBlob, SfluxPrc]] = Field(None, description='sflux prc source 2')

model_config class-attribute instance-attribute

model_config = ConfigDict(arbitrary_types_allowed=True, extra='ignore')

Functions

validate_air_fields

validate_air_fields()

Validate air fields after model creation.

Source code in rompy_schism/data.py
@model_validator(mode="after")
def validate_air_fields(self):
    """Validate air fields after model creation."""
    # Convert dictionary to SfluxAir if needed
    if isinstance(self.air_1, dict):
        try:
            # Import here to avoid circular import
            from rompy_schism.data import SfluxAir

            logger.info(
                f"Converting air_1 dictionary to SfluxAir object: {self.air_1}"
            )
            self.air_1 = SfluxAir(**self.air_1)
            logger.info("Successfully converted air_1 to SfluxAir instance")
        except Exception as e:
            logger.error(f"Error converting air_1 dictionary to SfluxAir: {e}")
            logger.error(f"Input data: {self.air_1}")
            # We'll let validation continue with the dictionary

    if isinstance(self.air_2, dict):
        try:
            from rompy_schism.data import SfluxAir

            logger.info(
                f"Converting air_2 dictionary to SfluxAir object: {self.air_2}"
            )
            self.air_2 = SfluxAir(**self.air_2)
            logger.info("Successfully converted air_2 to SfluxAir instance")
        except Exception as e:
            logger.error(f"Error converting air_2 dictionary to SfluxAir: {e}")
            logger.error(f"Input data: {self.air_2}")

    return self

get

get(destdir: str | Path, grid: Optional[SCHISMGrid] = None, time: Optional[TimeRange] = None) -> Path

Writes SCHISM sflux data from a dataset.

Parameters:

Name Type Description Default
destdir str | Path

The destination directory to write the sflux data.

required
grid Optional[SCHISMGrid]

The grid type. Defaults to None.

None
time Optional[TimeRange]

The time range. Defaults to None.

None

Returns:

Name Type Description
Path Path

The path to the written sflux data.

Source code in rompy_schism/data.py
def get(
    self,
    destdir: str | Path,
    grid: Optional[SCHISMGrid] = None,
    time: Optional[TimeRange] = None,
) -> Path:
    """Writes SCHISM sflux data from a dataset.

    Args:
        destdir (str | Path): The destination directory to write the sflux data.
        grid (Optional[SCHISMGrid], optional): The grid type. Defaults to None.
        time (Optional[TimeRange], optional): The time range. Defaults to None.

    Returns:
        Path: The path to the written sflux data.

    """
    ret = {}
    destdir = Path(destdir) / "sflux"
    destdir.mkdir(parents=True, exist_ok=True)
    namelistargs = {}

    # Collect information about active variables for logging
    active_variables = []
    source_info = {}

    for variable in ["air_1", "air_2", "rad_1", "rad_2", "prc_1", "prc_2"]:
        data = getattr(self, variable)
        if data is None:
            continue
        data.id = variable
        active_variables.append(variable)

        # Get source information
        if hasattr(data, "source") and hasattr(data.source, "uri"):
            source_info[variable] = str(data.source.uri)

        logger.debug(f"Processing {variable}")
        namelistargs.update(data.namelist)
        # Expand time by one day on each end
        if time is not None:
            time = TimeRange(
                start=time.start - pd.Timedelta(days=1),
                end=time.end + pd.Timedelta(days=1),
            )
        ret[variable] = data.get(destdir, grid, time)

    # Log summary of atmospheric data processing
    if active_variables:
        logger.info(f"  • Variables: {', '.join(active_variables)}")
        if source_info:
            unique_sources = list(set(source_info.values()))
            if len(unique_sources) == 1:
                logger.info(f"  • Source: {unique_sources[0]}")
            else:
                logger.info(f"  • Sources: {len(unique_sources)} files")
        logger.info(f"  • Output: {destdir}")

    ret["nml"] = Sflux_Inputs(**namelistargs).write_nml(destdir)
    return ret

check_weights

check_weights(v)

Check that relative weights for each pair add to 1.

Parameters:

Name Type Description Default
cls

The class.

required
v

The variable.

required

Raises:

Type Description
ValueError

If the relative weights for any variable do not add up to 1.0.

Source code in rompy_schism/data.py
@model_validator(mode="after")
def check_weights(v):
    """Check that relative weights for each pair add to 1.

    Args:
        cls: The class.
        v: The variable.

    Raises:
        ValueError: If the relative weights for any variable do not add up to 1.0.

    """
    for variable in ["air", "rad", "prc"]:
        weight = 0
        active = False
        for i in [1, 2]:
            data = getattr(v, f"{variable}_{i}")
            if data is None:
                continue
            if data.fail_if_missing:
                continue
            weight += data.relative_weight
            active = True
        if active and weight != 1.0:
            raise ValueError(
                f"Relative weights for {variable} do not add to 1.0: {weight}"
            )
        return v
    # SCHISM doesn't like scale_factor and add_offset attributes and requires Float64 values
    for var in ds.data_vars:
        # If the variable has scale_factor or add_offset attributes, remove them
        if "scale_factor" in ds[var].encoding:
            del ds[var].encoding["scale_factor"]
        if "add_offset" in ds[var].encoding:
            del ds[var].encoding["add_offset"]
        # set the data variable encoding to Float64
        ds[var].encoding["dtype"] = np.dtypes.Float64DType()

SCHISMDataWave

Bases: BoundaryWaveStation

This class is used to write wave spectral boundary data. Spectral data is extracted from the nearest points along the grid boundary

Source code in rompy_schism/data.py
class SCHISMDataWave(BoundaryWaveStation):
    """This class is used to write wave spectral boundary data. Spectral data is extracted
    from the nearest points along the grid boundary"""

    data_type: Literal["wave"] = Field(
        default="wave",
        description="Model type discriminator",
    )
    sel_method: Literal["idw", "nearest"] = Field(
        default="nearest",
        description="Method for selecting boundary points",
    )
    sel_method_kwargs: dict = Field(
        default={"unique": True},
        description="Keyword arguments for sel_method",
    )
    time_buffer: list[int] = Field(
        default=[0, 1],
        description="Number of source data timesteps to buffer the time range if `filter_time` is True",
    )

    def get(
        self,
        destdir: str | Path,
        grid: SCHISMGrid,
        time: Optional[TimeRange] = None,
    ) -> str:
        """Write the selected boundary data to a netcdf file.
        Parameters
        ----------
        destdir : str | Path
            Destination directory for the netcdf file.
        grid : SCHISMGrid
            Grid instance to use for selecting the boundary points.
        time: TimeRange, optional
            The times to filter the data to, only used if `self.crop_data` is True.

        Returns
        -------
        outfile : Path
            Path to the netcdf file.

        """
        logger.debug(f"Processing wave data: {self.id}")
        if self.crop_data and time is not None:
            self._filter_time(time)
        ds = self._sel_boundary(grid)
        outfile = Path(destdir) / f"{self.id}.nc"
        ds.spec.to_ww3(outfile)
        logger.debug(f"Saved wave data to {outfile}")
        return outfile

    @property
    def ds(self):
        """Return the filtered xarray dataset instance."""
        ds = super().ds
        for var in ds.data_vars:
            # If the variable has scale_factor or add_offset attributes, remove them
            if "scale_factor" in ds[var].encoding:
                del ds[var].encoding["scale_factor"]
            if "add_offset" in ds[var].encoding:
                del ds[var].encoding["add_offset"]
            # set the data variable encoding to Float64
            ds[var].encoding["dtype"] = np.dtypes.Float64DType()
        return ds

    def __str__(self):
        return "SCHISMDataWave"

Attributes

data_type class-attribute instance-attribute

data_type: Literal['wave'] = Field(default='wave', description='Model type discriminator')

sel_method class-attribute instance-attribute

sel_method: Literal['idw', 'nearest'] = Field(default='nearest', description='Method for selecting boundary points')

sel_method_kwargs class-attribute instance-attribute

sel_method_kwargs: dict = Field(default={'unique': True}, description='Keyword arguments for sel_method')

time_buffer class-attribute instance-attribute

time_buffer: list[int] = Field(default=[0, 1], description='Number of source data timesteps to buffer the time range if `filter_time` is True')

ds property

ds

Return the filtered xarray dataset instance.

Functions

get

get(destdir: str | Path, grid: SCHISMGrid, time: Optional[TimeRange] = None) -> str

Write the selected boundary data to a netcdf file. Parameters


destdir : str | Path Destination directory for the netcdf file. grid : SCHISMGrid Grid instance to use for selecting the boundary points. time: TimeRange, optional The times to filter the data to, only used if self.crop_data is True.

Returns

outfile : Path Path to the netcdf file.

Source code in rompy_schism/data.py
def get(
    self,
    destdir: str | Path,
    grid: SCHISMGrid,
    time: Optional[TimeRange] = None,
) -> str:
    """Write the selected boundary data to a netcdf file.
    Parameters
    ----------
    destdir : str | Path
        Destination directory for the netcdf file.
    grid : SCHISMGrid
        Grid instance to use for selecting the boundary points.
    time: TimeRange, optional
        The times to filter the data to, only used if `self.crop_data` is True.

    Returns
    -------
    outfile : Path
        Path to the netcdf file.

    """
    logger.debug(f"Processing wave data: {self.id}")
    if self.crop_data and time is not None:
        self._filter_time(time)
    ds = self._sel_boundary(grid)
    outfile = Path(destdir) / f"{self.id}.nc"
    ds.spec.to_ww3(outfile)
    logger.debug(f"Saved wave data to {outfile}")
    return outfile

SCHISMDataBoundaryConditions

Bases: RompyBaseModel

This class configures all boundary conditions for SCHISM including tidal, ocean, river, and nested model boundaries.

It provides a unified interface for specifying boundary conditions and their data sources, replacing the separate tides and ocean configurations.

Source code in rompy_schism/data.py
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
class SCHISMDataBoundaryConditions(RompyBaseModel):
    """
    This class configures all boundary conditions for SCHISM including tidal,
    ocean, river, and nested model boundaries.

    It provides a unified interface for specifying boundary conditions and their
    data sources, replacing the separate tides and ocean configurations.
    """

    # Allow arbitrary types for schema generation
    model_config = ConfigDict(arbitrary_types_allowed=True)

    data_type: Literal["boundary_conditions"] = Field(
        default="boundary_conditions",
        description="Model type discriminator",
    )

    # Tidal dataset specification
    tidal_data: Optional[TidalDataset] = Field(
        None,
        description="Tidal forcing dataset",
    )

    # Boundary configurations with integrated data sources
    boundaries: Dict[int, BoundarySetupWithSource] = Field(
        default_factory=dict,
        description="Boundary configuration by boundary index",
    )

    # Predefined configuration types
    setup_type: Optional[Literal["tidal", "hybrid", "river", "nested"]] = Field(
        None, description="Predefined boundary setup type"
    )

    # Hotstart configuration
    hotstart_config: Optional[HotstartConfig] = Field(
        None, description="Configuration for hotstart file generation"
    )

    @model_validator(mode="before")
    @classmethod
    def convert_numpy_types(cls, data):
        """Convert any numpy values to Python native types"""
        if not isinstance(data, dict):
            return data

        for key, value in list(data.items()):
            if isinstance(value, (np.bool_, np.integer, np.floating, np.ndarray)):
                data[key] = to_python_type(value)
        return data

    @model_validator(mode="after")
    def validate_tidal_data(self):
        """Ensure tidal data is provided when needed for TIDAL or TIDALSPACETIME boundaries."""
        boundaries = self.boundaries or {}
        needs_tidal_data = False

        # Check setup_type first
        if self.setup_type in ["tidal", "hybrid"]:
            needs_tidal_data = True

        # Then check individual boundaries
        for setup in boundaries.values():
            if (
                hasattr(setup, "elev_type")
                and setup.elev_type
                in [ElevationType.HARMONIC, ElevationType.HARMONICEXTERNAL]
            ) or (
                hasattr(setup, "vel_type")
                and setup.vel_type
                in [VelocityType.HARMONIC, VelocityType.HARMONICEXTERNAL]
            ):
                needs_tidal_data = True
                break

        if needs_tidal_data and not self.tidal_data:
            raise ValueError(
                "Tidal data is required for HARMONIC or HARMONICEXTERNAL boundary types but was not provided"
            )

        return self

    @model_validator(mode="after")
    def validate_setup_type(self):
        """Validate setup type specific requirements."""
        # Skip validation if setup_type is not set
        if not self.setup_type:
            return self

        if self.setup_type in ["tidal", "hybrid"]:
            if not self.tidal_data:
                raise ValueError(
                    "tidal_data is required for tidal or hybrid setup_type"
                )

        elif self.setup_type == "river":
            if self.boundaries:
                has_flow = any(
                    hasattr(s, "const_flow") and s.const_flow is not None
                    for s in self.boundaries.values()
                )
                if not has_flow:
                    raise ValueError(
                        "At least one boundary should have const_flow for river setup_type"
                    )

        elif self.setup_type == "nested":
            if self.boundaries:
                for idx, setup in self.boundaries.items():
                    if (
                        hasattr(setup, "vel_type")
                        and setup.vel_type == VelocityType.RELAXED
                    ):
                        if not hasattr(setup, "inflow_relax") or not hasattr(
                            setup, "outflow_relax"
                        ):
                            logger.warning(
                                f"inflow_relax and outflow_relax are recommended for nested setup_type in boundary {idx}"
                            )
        else:
            raise ValueError(
                f"Unknown setup_type: {self.setup_type}. Expected one of: tidal, hybrid, river, nested"
            )

        return self

    def _create_boundary_config(self, grid):
        """Create a TidalBoundary object based on the configuration."""
        # Get tidal data paths
        if self.tidal_data:
            if (
                hasattr(self.tidal_data, "tidal_database")
                and self.tidal_data.tidal_database
            ):
                str(self.tidal_data.tidal_database)

        # Ensure boundary information is computed
        if hasattr(grid.pylibs_hgrid, "compute_bnd"):
            grid.pylibs_hgrid.compute_bnd()
        else:
            logger.warning(
                "Grid object doesn't have compute_bnd method. Boundary information may be missing."
            )

        # Create a new TidalBoundary with all the configuration
        # Ensure boundary information is computed before creating the boundary
        if not hasattr(grid.pylibs_hgrid, "nob") or not hasattr(
            grid.pylibs_hgrid, "nobn"
        ):
            logger.info("Computing boundary information before creating TidalBoundary")
            # First try compute_bnd if available
            if hasattr(grid.pylibs_hgrid, "compute_bnd"):
                grid.pylibs_hgrid.compute_bnd()

            # Then try compute_all if nob is still missing
            if not hasattr(grid.pylibs_hgrid, "nob") and hasattr(
                grid.pylibs_hgrid, "compute_all"
            ):
                if hasattr(grid.pylibs_hgrid, "compute_all"):
                    grid.pylibs_hgrid.compute_all()

        # Verify boundary attributes are available
        if not hasattr(grid.pylibs_hgrid, "nob"):
            logger.error("Failed to set 'nob' attribute on grid.pylibs_hgrid")
            raise AttributeError(
                "Missing required 'nob' attribute on grid.pylibs_hgrid"
            )

        # Create TidalBoundary with pre-computed grid to avoid losing boundary info
        # Get the grid path for TidalBoundary
        grid_path = (
            str(grid.hgrid.path)
            if hasattr(grid, "hgrid") and hasattr(grid.hgrid, "path")
            else None
        )
        if grid_path is None:
            # Create a temporary file with the grid if needed
            import tempfile

            temp_file = tempfile.NamedTemporaryFile(suffix=".gr3", delete=False)
            temp_path = temp_file.name
            temp_file.close()
            grid.pylibs_hgrid.write_hgrid(temp_path)
            grid_path = temp_path

        boundary = BoundaryHandler(grid_path=grid_path, tidal_data=self.tidal_data)

        # Replace the TidalBoundary's grid with our pre-computed one to preserve boundary info
        boundary.grid = grid.pylibs_hgrid

        # Configure each boundary segment
        for idx, setup in self.boundaries.items():
            boundary_config = setup.to_boundary_config()
            boundary.set_boundary_config(idx, boundary_config)

        return boundary

    def get(
        self,
        destdir: str | Path,
        grid: SCHISMGrid,
        time: TimeRange,
    ) -> Dict[str, str]:
        """
        Process all boundary data and generate necessary input files.

        Parameters
        ----------
        destdir : str | Path
            Destination directory
        grid : SCHISMGrid
            SCHISM grid instance
        time : TimeRange
            Time range for the simulation

        Returns
        -------
        Dict[str, str]
            Paths to generated files
        """
        # Processing boundary conditions

        # Convert destdir to Path object
        destdir = Path(destdir)

        # Create destdir if it doesn't exist
        if not destdir.exists():
            logger.info(f"Creating destination directory: {destdir}")
            destdir.mkdir(parents=True, exist_ok=True)

        # # 1. Process tidal data if needed
        if self.tidal_data:
            logger.info(
                f"{ARROW} Processing tidal constituents: {', '.join(self.tidal_data.constituents) if hasattr(self.tidal_data, 'constituents') else 'default'}"
            )
            self.tidal_data.get(grid)

        # 2. Create boundary condition file (bctides.in)
        boundary = self._create_boundary_config(grid)

        # Set start time and run duration
        start_time = time.start
        if time.end is not None and time.start is not None:
            run_days = (
                time.end - time.start
            ).total_seconds() / 86400.0  # Convert to days
        else:
            run_days = 1.0  # Default to 1 day if time is not properly specified
        boundary.set_run_parameters(start_time, run_days)

        # Generate bctides.in file
        bctides_path = destdir / "bctides.in"
        logger.info(f"{ARROW} Generating boundary condition file: bctides.in")

        # Ensure grid object has complete boundary information before writing
        if hasattr(grid.pylibs_hgrid, "compute_all"):
            grid.pylibs_hgrid.compute_all()

        # Double-check all required attributes are present
        required_attrs = ["nob", "nobn", "iobn"]
        missing_attrs = [
            attr
            for attr in required_attrs
            if not (grid.pylibs_hgrid and hasattr(grid.pylibs_hgrid, attr))
        ]
        if missing_attrs:
            error_msg = (
                f"Grid is missing required attributes: {', '.join(missing_attrs)}"
            )
            logger.error(error_msg)
            raise AttributeError(error_msg)

        # Write the boundary file - no fallbacks
        boundary.write_boundary_file(bctides_path)
        logger.info(f"{ARROW} Boundary conditions written successfully")

        # 3. Process ocean data based on boundary configurations
        processed_files = {"bctides": str(bctides_path)}

        # Collect variables to process and source information for logging
        variables_to_process = []
        source_files = set()
        for idx, setup in self.boundaries.items():
            if (
                setup.elev_type
                in [ElevationType.EXTERNAL, ElevationType.HARMONICEXTERNAL]
                and setup.elev_source
            ):
                variables_to_process.append("elevation")
                if hasattr(setup.elev_source, "source") and hasattr(
                    setup.elev_source.source, "uri"
                ):
                    source_files.add(str(setup.elev_source.source.uri))
            if (
                setup.vel_type
                in [
                    VelocityType.EXTERNAL,
                    VelocityType.HARMONICEXTERNAL,
                    VelocityType.RELAXED,
                ]
                and setup.vel_source
            ):
                variables_to_process.append("velocity")
                if hasattr(setup.vel_source, "source") and hasattr(
                    setup.vel_source.source, "uri"
                ):
                    source_files.add(str(setup.vel_source.source.uri))
            if setup.temp_type == TracerType.EXTERNAL and setup.temp_source:
                variables_to_process.append("temperature")
                if hasattr(setup.temp_source, "source") and hasattr(
                    setup.temp_source.source, "uri"
                ):
                    source_files.add(str(setup.temp_source.source.uri))
            if setup.salt_type == TracerType.EXTERNAL and setup.salt_source:
                variables_to_process.append("salinity")
                if hasattr(setup.salt_source, "source") and hasattr(
                    setup.salt_source.source, "uri"
                ):
                    source_files.add(str(setup.salt_source.source.uri))

        if variables_to_process:
            unique_vars = list(
                dict.fromkeys(variables_to_process)
            )  # Remove duplicates while preserving order
            logger.info(f"{ARROW} Processing boundary data: {', '.join(unique_vars)}")
            if source_files:
                if len(source_files) == 1:
                    logger.info(f"  • Source: {list(source_files)[0]}")
                else:
                    logger.info(f"  • Sources: {len(source_files)} files")

        # Process each data source based on the boundary type
        for idx, setup in self.boundaries.items():
            # Process elevation data if needed
            if setup.elev_type in [
                ElevationType.EXTERNAL,
                ElevationType.HARMONICEXTERNAL,
            ]:
                if setup.elev_source:
                    if (
                        hasattr(setup.elev_source, "data_type")
                        and setup.elev_source.data_type == "boundary"
                    ):
                        # Process using SCHISMDataBoundary interface
                        setup.elev_source.id = "elev2D"  # Set the ID for the boundary
                        file_path = setup.elev_source.get(destdir, grid, time)
                    else:
                        # Process using DataBlob interface
                        file_path = setup.elev_source.get(str(destdir))
                    processed_files[f"elev_boundary_{idx}"] = file_path

            # Process velocity data if needed
            if setup.vel_type in [
                VelocityType.EXTERNAL,
                VelocityType.HARMONICEXTERNAL,
                VelocityType.RELAXED,
            ]:
                if setup.vel_source:
                    if (
                        hasattr(setup.vel_source, "data_type")
                        and setup.vel_source.data_type == "boundary"
                    ):
                        # Process using SCHISMDataBoundary interface
                        setup.vel_source.id = "uv3D"  # Set the ID for the boundary
                        file_path = setup.vel_source.get(destdir, grid, time)
                    else:
                        # Process using DataBlob interface
                        file_path = setup.vel_source.get(str(destdir))
                    processed_files[f"vel_boundary_{idx}"] = file_path

            # Process temperature data if needed
            if setup.temp_type == TracerType.EXTERNAL:
                if setup.temp_source:
                    if (
                        hasattr(setup.temp_source, "data_type")
                        and setup.temp_source.data_type == "boundary"
                    ):
                        # Process using SCHISMDataBoundary interface
                        setup.temp_source.id = "TEM_3D"  # Set the ID for the boundary
                        file_path = setup.temp_source.get(destdir, grid, time)
                    else:
                        # Process using DataBlob interface
                        file_path = setup.temp_source.get(str(destdir))
                    processed_files[f"temp_boundary_{idx}"] = file_path

            # Process salinity data if needed
            if setup.salt_type == TracerType.EXTERNAL:
                if setup.salt_source:
                    if (
                        hasattr(setup.salt_source, "data_type")
                        and setup.salt_source.data_type == "boundary"
                    ):
                        # Process using SCHISMDataBoundary interface
                        setup.salt_source.id = "SAL_3D"  # Set the ID for the boundary
                        file_path = setup.salt_source.get(destdir, grid, time)
                    else:
                        # Process using DataBlob interface
                        file_path = setup.salt_source.get(str(destdir))
                    processed_files[f"salt_boundary_{idx}"] = file_path

        # Generate hotstart file if configured
        if self.hotstart_config and self.hotstart_config.enabled:
            logger.info(f"{ARROW} Generating hotstart file")
            hotstart_path = self._generate_hotstart(destdir, grid, time)
            processed_files["hotstart"] = hotstart_path
            logger.info(f"  • Output: {hotstart_path}")

        # Log summary of processed files with more details
        boundary_data_files = [f for k, f in processed_files.items() if "boundary" in k]
        if boundary_data_files:
            logger.info(
                f"  • Files: {', '.join([Path(f).name for f in boundary_data_files])}"
            )

        return processed_files

    def _generate_hotstart(
        self,
        destdir: Union[str, Path],
        grid: SCHISMGrid,
        time: Optional[TimeRange] = None,
    ) -> str:
        """
        Generate hotstart file using boundary condition data sources.

        Args:
            destdir: Destination directory for the hotstart file
            grid: SCHISM grid object
            time: Time range for the data

        Returns:
            Path to the generated hotstart file
        """
        from rompy_schism.hotstart import SCHISMDataHotstart

        # Find a boundary that has both temperature and salinity sources
        temp_source = None
        salt_source = None

        for boundary_config in self.boundaries.values():
            if boundary_config.temp_source is not None:
                temp_source = boundary_config.temp_source
            if boundary_config.salt_source is not None:
                salt_source = boundary_config.salt_source

            # If we found both, we can proceed
            if temp_source is not None and salt_source is not None:
                break

        if temp_source is None or salt_source is None:
            raise ValueError(
                "Hotstart generation requires both temperature and salinity sources "
                "to be configured in boundary conditions"
            )

        # Create hotstart instance using the first available source
        # (assuming temp and salt sources point to the same dataset)
        # Include both temperature and salinity variables for hotstart generation
        temp_var_name = (
            self.hotstart_config.temp_var if self.hotstart_config else "temperature"
        )
        salt_var_name = (
            self.hotstart_config.salt_var if self.hotstart_config else "salinity"
        )

        # Log hotstart generation details
        logger.info(f"  • Variables: {temp_var_name}, {salt_var_name}")
        if hasattr(temp_source, "source") and hasattr(temp_source.source, "uri"):
            logger.info(f"  • Source: {temp_source.source.uri}")

        hotstart_data = SCHISMDataHotstart(
            source=temp_source.source,
            variables=[temp_var_name, salt_var_name],
            coords=getattr(temp_source, "coords", None),
            temp_var=temp_var_name,
            salt_var=salt_var_name,
            time_offset=(
                self.hotstart_config.time_offset if self.hotstart_config else 0.0
            ),
            time_base=(
                self.hotstart_config.time_base
                if self.hotstart_config
                else datetime(2000, 1, 1)
            ),
            output_filename=(
                self.hotstart_config.output_filename
                if self.hotstart_config
                else "hotstart.nc"
            ),
        )

        return hotstart_data.get(str(destdir), grid=grid, time=time)

Attributes

model_config class-attribute instance-attribute

model_config = ConfigDict(arbitrary_types_allowed=True)

data_type class-attribute instance-attribute

data_type: Literal['boundary_conditions'] = Field(default='boundary_conditions', description='Model type discriminator')

tidal_data class-attribute instance-attribute

tidal_data: Optional[TidalDataset] = Field(None, description='Tidal forcing dataset')

boundaries class-attribute instance-attribute

boundaries: Dict[int, BoundarySetupWithSource] = Field(default_factory=dict, description='Boundary configuration by boundary index')

setup_type class-attribute instance-attribute

setup_type: Optional[Literal['tidal', 'hybrid', 'river', 'nested']] = Field(None, description='Predefined boundary setup type')

hotstart_config class-attribute instance-attribute

hotstart_config: Optional[HotstartConfig] = Field(None, description='Configuration for hotstart file generation')

Functions

convert_numpy_types classmethod

convert_numpy_types(data)

Convert any numpy values to Python native types

Source code in rompy_schism/data.py
@model_validator(mode="before")
@classmethod
def convert_numpy_types(cls, data):
    """Convert any numpy values to Python native types"""
    if not isinstance(data, dict):
        return data

    for key, value in list(data.items()):
        if isinstance(value, (np.bool_, np.integer, np.floating, np.ndarray)):
            data[key] = to_python_type(value)
    return data

validate_tidal_data

validate_tidal_data()

Ensure tidal data is provided when needed for TIDAL or TIDALSPACETIME boundaries.

Source code in rompy_schism/data.py
@model_validator(mode="after")
def validate_tidal_data(self):
    """Ensure tidal data is provided when needed for TIDAL or TIDALSPACETIME boundaries."""
    boundaries = self.boundaries or {}
    needs_tidal_data = False

    # Check setup_type first
    if self.setup_type in ["tidal", "hybrid"]:
        needs_tidal_data = True

    # Then check individual boundaries
    for setup in boundaries.values():
        if (
            hasattr(setup, "elev_type")
            and setup.elev_type
            in [ElevationType.HARMONIC, ElevationType.HARMONICEXTERNAL]
        ) or (
            hasattr(setup, "vel_type")
            and setup.vel_type
            in [VelocityType.HARMONIC, VelocityType.HARMONICEXTERNAL]
        ):
            needs_tidal_data = True
            break

    if needs_tidal_data and not self.tidal_data:
        raise ValueError(
            "Tidal data is required for HARMONIC or HARMONICEXTERNAL boundary types but was not provided"
        )

    return self

validate_setup_type

validate_setup_type()

Validate setup type specific requirements.

Source code in rompy_schism/data.py
@model_validator(mode="after")
def validate_setup_type(self):
    """Validate setup type specific requirements."""
    # Skip validation if setup_type is not set
    if not self.setup_type:
        return self

    if self.setup_type in ["tidal", "hybrid"]:
        if not self.tidal_data:
            raise ValueError(
                "tidal_data is required for tidal or hybrid setup_type"
            )

    elif self.setup_type == "river":
        if self.boundaries:
            has_flow = any(
                hasattr(s, "const_flow") and s.const_flow is not None
                for s in self.boundaries.values()
            )
            if not has_flow:
                raise ValueError(
                    "At least one boundary should have const_flow for river setup_type"
                )

    elif self.setup_type == "nested":
        if self.boundaries:
            for idx, setup in self.boundaries.items():
                if (
                    hasattr(setup, "vel_type")
                    and setup.vel_type == VelocityType.RELAXED
                ):
                    if not hasattr(setup, "inflow_relax") or not hasattr(
                        setup, "outflow_relax"
                    ):
                        logger.warning(
                            f"inflow_relax and outflow_relax are recommended for nested setup_type in boundary {idx}"
                        )
    else:
        raise ValueError(
            f"Unknown setup_type: {self.setup_type}. Expected one of: tidal, hybrid, river, nested"
        )

    return self

get

get(destdir: str | Path, grid: SCHISMGrid, time: TimeRange) -> Dict[str, str]

Process all boundary data and generate necessary input files.

Parameters

destdir : str | Path Destination directory grid : SCHISMGrid SCHISM grid instance time : TimeRange Time range for the simulation

Returns

Dict[str, str] Paths to generated files

Source code in rompy_schism/data.py
def get(
    self,
    destdir: str | Path,
    grid: SCHISMGrid,
    time: TimeRange,
) -> Dict[str, str]:
    """
    Process all boundary data and generate necessary input files.

    Parameters
    ----------
    destdir : str | Path
        Destination directory
    grid : SCHISMGrid
        SCHISM grid instance
    time : TimeRange
        Time range for the simulation

    Returns
    -------
    Dict[str, str]
        Paths to generated files
    """
    # Processing boundary conditions

    # Convert destdir to Path object
    destdir = Path(destdir)

    # Create destdir if it doesn't exist
    if not destdir.exists():
        logger.info(f"Creating destination directory: {destdir}")
        destdir.mkdir(parents=True, exist_ok=True)

    # # 1. Process tidal data if needed
    if self.tidal_data:
        logger.info(
            f"{ARROW} Processing tidal constituents: {', '.join(self.tidal_data.constituents) if hasattr(self.tidal_data, 'constituents') else 'default'}"
        )
        self.tidal_data.get(grid)

    # 2. Create boundary condition file (bctides.in)
    boundary = self._create_boundary_config(grid)

    # Set start time and run duration
    start_time = time.start
    if time.end is not None and time.start is not None:
        run_days = (
            time.end - time.start
        ).total_seconds() / 86400.0  # Convert to days
    else:
        run_days = 1.0  # Default to 1 day if time is not properly specified
    boundary.set_run_parameters(start_time, run_days)

    # Generate bctides.in file
    bctides_path = destdir / "bctides.in"
    logger.info(f"{ARROW} Generating boundary condition file: bctides.in")

    # Ensure grid object has complete boundary information before writing
    if hasattr(grid.pylibs_hgrid, "compute_all"):
        grid.pylibs_hgrid.compute_all()

    # Double-check all required attributes are present
    required_attrs = ["nob", "nobn", "iobn"]
    missing_attrs = [
        attr
        for attr in required_attrs
        if not (grid.pylibs_hgrid and hasattr(grid.pylibs_hgrid, attr))
    ]
    if missing_attrs:
        error_msg = (
            f"Grid is missing required attributes: {', '.join(missing_attrs)}"
        )
        logger.error(error_msg)
        raise AttributeError(error_msg)

    # Write the boundary file - no fallbacks
    boundary.write_boundary_file(bctides_path)
    logger.info(f"{ARROW} Boundary conditions written successfully")

    # 3. Process ocean data based on boundary configurations
    processed_files = {"bctides": str(bctides_path)}

    # Collect variables to process and source information for logging
    variables_to_process = []
    source_files = set()
    for idx, setup in self.boundaries.items():
        if (
            setup.elev_type
            in [ElevationType.EXTERNAL, ElevationType.HARMONICEXTERNAL]
            and setup.elev_source
        ):
            variables_to_process.append("elevation")
            if hasattr(setup.elev_source, "source") and hasattr(
                setup.elev_source.source, "uri"
            ):
                source_files.add(str(setup.elev_source.source.uri))
        if (
            setup.vel_type
            in [
                VelocityType.EXTERNAL,
                VelocityType.HARMONICEXTERNAL,
                VelocityType.RELAXED,
            ]
            and setup.vel_source
        ):
            variables_to_process.append("velocity")
            if hasattr(setup.vel_source, "source") and hasattr(
                setup.vel_source.source, "uri"
            ):
                source_files.add(str(setup.vel_source.source.uri))
        if setup.temp_type == TracerType.EXTERNAL and setup.temp_source:
            variables_to_process.append("temperature")
            if hasattr(setup.temp_source, "source") and hasattr(
                setup.temp_source.source, "uri"
            ):
                source_files.add(str(setup.temp_source.source.uri))
        if setup.salt_type == TracerType.EXTERNAL and setup.salt_source:
            variables_to_process.append("salinity")
            if hasattr(setup.salt_source, "source") and hasattr(
                setup.salt_source.source, "uri"
            ):
                source_files.add(str(setup.salt_source.source.uri))

    if variables_to_process:
        unique_vars = list(
            dict.fromkeys(variables_to_process)
        )  # Remove duplicates while preserving order
        logger.info(f"{ARROW} Processing boundary data: {', '.join(unique_vars)}")
        if source_files:
            if len(source_files) == 1:
                logger.info(f"  • Source: {list(source_files)[0]}")
            else:
                logger.info(f"  • Sources: {len(source_files)} files")

    # Process each data source based on the boundary type
    for idx, setup in self.boundaries.items():
        # Process elevation data if needed
        if setup.elev_type in [
            ElevationType.EXTERNAL,
            ElevationType.HARMONICEXTERNAL,
        ]:
            if setup.elev_source:
                if (
                    hasattr(setup.elev_source, "data_type")
                    and setup.elev_source.data_type == "boundary"
                ):
                    # Process using SCHISMDataBoundary interface
                    setup.elev_source.id = "elev2D"  # Set the ID for the boundary
                    file_path = setup.elev_source.get(destdir, grid, time)
                else:
                    # Process using DataBlob interface
                    file_path = setup.elev_source.get(str(destdir))
                processed_files[f"elev_boundary_{idx}"] = file_path

        # Process velocity data if needed
        if setup.vel_type in [
            VelocityType.EXTERNAL,
            VelocityType.HARMONICEXTERNAL,
            VelocityType.RELAXED,
        ]:
            if setup.vel_source:
                if (
                    hasattr(setup.vel_source, "data_type")
                    and setup.vel_source.data_type == "boundary"
                ):
                    # Process using SCHISMDataBoundary interface
                    setup.vel_source.id = "uv3D"  # Set the ID for the boundary
                    file_path = setup.vel_source.get(destdir, grid, time)
                else:
                    # Process using DataBlob interface
                    file_path = setup.vel_source.get(str(destdir))
                processed_files[f"vel_boundary_{idx}"] = file_path

        # Process temperature data if needed
        if setup.temp_type == TracerType.EXTERNAL:
            if setup.temp_source:
                if (
                    hasattr(setup.temp_source, "data_type")
                    and setup.temp_source.data_type == "boundary"
                ):
                    # Process using SCHISMDataBoundary interface
                    setup.temp_source.id = "TEM_3D"  # Set the ID for the boundary
                    file_path = setup.temp_source.get(destdir, grid, time)
                else:
                    # Process using DataBlob interface
                    file_path = setup.temp_source.get(str(destdir))
                processed_files[f"temp_boundary_{idx}"] = file_path

        # Process salinity data if needed
        if setup.salt_type == TracerType.EXTERNAL:
            if setup.salt_source:
                if (
                    hasattr(setup.salt_source, "data_type")
                    and setup.salt_source.data_type == "boundary"
                ):
                    # Process using SCHISMDataBoundary interface
                    setup.salt_source.id = "SAL_3D"  # Set the ID for the boundary
                    file_path = setup.salt_source.get(destdir, grid, time)
                else:
                    # Process using DataBlob interface
                    file_path = setup.salt_source.get(str(destdir))
                processed_files[f"salt_boundary_{idx}"] = file_path

    # Generate hotstart file if configured
    if self.hotstart_config and self.hotstart_config.enabled:
        logger.info(f"{ARROW} Generating hotstart file")
        hotstart_path = self._generate_hotstart(destdir, grid, time)
        processed_files["hotstart"] = hotstart_path
        logger.info(f"  • Output: {hotstart_path}")

    # Log summary of processed files with more details
    boundary_data_files = [f for k, f in processed_files.items() if "boundary" in k]
    if boundary_data_files:
        logger.info(
            f"  • Files: {', '.join([Path(f).name for f in boundary_data_files])}"
        )

    return processed_files

HotstartConfig

Bases: RompyBaseModel

Configuration for generating SCHISM hotstart files.

This class specifies parameters for creating hotstart.nc files from temperature and salinity data sources already configured in boundary conditions.

Source code in rompy_schism/data.py
class HotstartConfig(RompyBaseModel):
    """
    Configuration for generating SCHISM hotstart files.

    This class specifies parameters for creating hotstart.nc files from
    temperature and salinity data sources already configured in boundary conditions.
    """

    enabled: bool = Field(
        default=False, description="Whether to generate hotstart file"
    )
    temp_var: str = Field(
        default="temperature",
        description="Name of temperature variable in source dataset",
    )
    salt_var: str = Field(
        default="salinity", description="Name of salinity variable in source dataset"
    )
    time_offset: float = Field(
        default=0.0, description="Offset to add to source time values (in days)"
    )
    time_base: datetime = Field(
        default=datetime(2000, 1, 1), description="Base time for source time values"
    )
    output_filename: str = Field(
        default="hotstart.nc", description="Name of the output hotstart file"
    )

Attributes

enabled class-attribute instance-attribute

enabled: bool = Field(default=False, description='Whether to generate hotstart file')

temp_var class-attribute instance-attribute

temp_var: str = Field(default='temperature', description='Name of temperature variable in source dataset')

salt_var class-attribute instance-attribute

salt_var: str = Field(default='salinity', description='Name of salinity variable in source dataset')

time_offset class-attribute instance-attribute

time_offset: float = Field(default=0.0, description='Offset to add to source time values (in days)')

time_base class-attribute instance-attribute

time_base: datetime = Field(default=datetime(2000, 1, 1), description='Base time for source time values')

output_filename class-attribute instance-attribute

output_filename: str = Field(default='hotstart.nc', description='Name of the output hotstart file')

SCHISMData

Bases: RompyBaseModel

This class is used to gather all required input forcing for SCHISM

Source code in rompy_schism/data.py
class SCHISMData(RompyBaseModel):
    """
    This class is used to gather all required input forcing for SCHISM
    """

    data_type: Literal["schism"] = Field(
        default="schism",
        description="Model type discriminator",
    )
    atmos: Optional[SCHISMDataSflux] = Field(None, description="atmospheric data")
    wave: Optional[Union[DataBlob, SCHISMDataWave]] = Field(
        None, description="wave data"
    )
    boundary_conditions: Optional["SCHISMDataBoundaryConditions"] = Field(
        None, description="unified boundary conditions (replaces tides and ocean)"
    )

    def get(
        self,
        destdir: str | Path,
        grid: SCHISMGrid,
        time: TimeRange,
    ) -> Dict[str, Any]:
        """
        Process all SCHISM forcing data and generate necessary input files.

        Parameters
        ----------
        destdir : str | Path
            Destination directory
        grid : SCHISMGrid
            SCHISM grid instance
        time : TimeRange
            Time range for the simulation

        Returns
        -------
        Dict[str, Any]
            Paths to generated files for each data component
        """
        from rompy.formatting import ARROW

        # Convert destdir to Path object
        destdir = Path(destdir)

        # Create destdir if it doesn't exist
        if not destdir.exists():
            destdir.mkdir(parents=True, exist_ok=True)

        results = {}

        # Process atmospheric data
        if self.atmos:
            logger.info(f"{ARROW} Processing atmospheric forcing data")
            results["atmos"] = self.atmos.get(destdir, grid, time)
            logger.info(f"{ARROW} Atmospheric data processed successfully")

        # Process wave data
        if self.wave:
            logger.info(f"{ARROW} Processing wave boundary data")
            # Get source information
            if hasattr(self.wave, "source") and hasattr(self.wave.source, "uri"):
                logger.info(f"  • Source: {self.wave.source.uri}")
            elif hasattr(self.wave, "source") and hasattr(
                self.wave.source, "catalog_uri"
            ):
                logger.info(
                    f"  • Source: {self.wave.source.catalog_uri} (dataset: {getattr(self.wave.source, 'dataset_id', 'unknown')})"
                )

            results["wave"] = self.wave.get(destdir, grid, time)
            logger.info(f"  • Output: {results['wave']}")
            logger.info(f"{ARROW} Wave data processed successfully")

        # Process boundary conditions
        if self.boundary_conditions:
            logger.info(f"{ARROW} Processing boundary conditions")
            results["boundary_conditions"] = self.boundary_conditions.get(
                destdir, grid, time
            )
            logger.info(f"{ARROW} Boundary conditions processed successfully")

        return results

    def _format_value(self, obj):
        """Custom formatter for SCHISMData values.

        This method provides special formatting for specific types used in
        SCHISMData such as atmospheric, wave, and boundary data components.

        Args:
            obj: The object to format

        Returns:
            A formatted string or None to use default formatting
        """
        # Import specific types and formatting utilities
        from rompy.formatting import get_formatted_header_footer
        from rompy.logging import LoggingConfig

        # Get ASCII mode setting from LoggingConfig
        logging_config = LoggingConfig()
        USE_ASCII_ONLY = logging_config.use_ascii

        # Format SCHISMData (self-formatting)
        if isinstance(obj, SCHISMData):
            header, footer, bullet = get_formatted_header_footer(
                title="SCHISM DATA CONFIGURATION", use_ascii=USE_ASCII_ONLY
            )

            lines = [header]

            # Count and list data components
            components = {}
            if hasattr(obj, "atmos") and obj.atmos is not None:
                components["Atmospheric"] = type(obj.atmos).__name__
                # Add details for atmospheric data
                if hasattr(obj.atmos, "air_1") and obj.atmos.air_1 is not None:
                    air_sources = 1
                    if hasattr(obj.atmos, "air_2") and obj.atmos.air_2 is not None:
                        air_sources = 2
                    lines.append(f"      Air sources: {air_sources}")

                if hasattr(obj.atmos, "rad_1") and obj.atmos.rad_1 is not None:
                    rad_sources = 1
                    if hasattr(obj.atmos, "rad_2") and obj.atmos.rad_2 is not None:
                        rad_sources = 2
                    lines.append(f"      Radiation sources: {rad_sources}")

            if hasattr(obj, "wave") and obj.wave is not None:
                components["Wave"] = type(obj.wave).__name__

            if (
                hasattr(obj, "boundary_conditions")
                and obj.boundary_conditions is not None
            ):
                components["Boundary Conditions"] = type(
                    obj.boundary_conditions
                ).__name__

            for comp_name, comp_type in components.items():
                lines.append(f"  {bullet} {comp_name}: {comp_type}")

            if not components:
                lines.append(f"  {bullet} No data components configured")

            lines.append(footer)
            return "\n".join(lines)

        # Format SCHISMDataSflux
        if isinstance(obj, SCHISMDataSflux):
            header, footer, bullet = get_formatted_header_footer(
                title="ATMOSPHERIC DATA (SFLUX)", use_ascii=USE_ASCII_ONLY
            )

            lines = [header]

            # Count air sources
            air_sources = 0
            if hasattr(obj, "air_1") and obj.air_1 is not None:
                air_sources += 1
            if hasattr(obj, "air_2") and obj.air_2 is not None:
                air_sources += 1

            if air_sources > 0:
                lines.append(f"  {bullet} Air sources: {air_sources}")

            # Count radiation sources
            rad_sources = 0
            if hasattr(obj, "rad_1") and obj.rad_1 is not None:
                rad_sources += 1
            if hasattr(obj, "rad_2") and obj.rad_2 is not None:
                rad_sources += 1

            if rad_sources > 0:
                lines.append(f"  {bullet} Radiation sources: {rad_sources}")

            # Check for precipitation
            if hasattr(obj, "prc_1") and obj.prc_1 is not None:
                lines.append(f"  {bullet} Precipitation: Available")

            lines.append(footer)
            return "\n".join(lines)

        # Format SCHISMDataWave
        if isinstance(obj, SCHISMDataWave):
            header, footer, bullet = get_formatted_header_footer(
                title="WAVE DATA", use_ascii=USE_ASCII_ONLY
            )

            lines = [header]

            if hasattr(obj, "sel_method"):
                lines.append(f"  {bullet} Selection method: {obj.sel_method}")

            if hasattr(obj, "source") and obj.source is not None:
                source_type = type(obj.source).__name__
                lines.append(f"  {bullet} Source: {source_type}")

            lines.append(footer)
            return "\n".join(lines)

        # Format SCHISMDataBoundaryConditions
        if isinstance(obj, SCHISMDataBoundaryConditions):
            header, footer, bullet = get_formatted_header_footer(
                title="BOUNDARY CONDITIONS", use_ascii=USE_ASCII_ONLY
            )

            lines = [header]

            # Count boundary setups
            boundary_count = 0
            if hasattr(obj, "boundaries") and obj.boundaries is not None:
                if isinstance(obj.boundaries, list):
                    boundary_count = len(obj.boundaries)
                else:
                    boundary_count = 1

            if boundary_count > 0:
                lines.append(f"  {bullet} Boundary setups: {boundary_count}")

            # Check for tidal components
            if hasattr(obj, "tidal") and obj.tidal is not None:
                lines.append(f"  {bullet} Tidal forcing: Available")

            lines.append(footer)
            return "\n".join(lines)

        # Use the new formatting framework
        from rompy.formatting import format_value

        return format_value(obj)

Attributes

data_type class-attribute instance-attribute

data_type: Literal['schism'] = Field(default='schism', description='Model type discriminator')

atmos class-attribute instance-attribute

atmos: Optional[SCHISMDataSflux] = Field(None, description='atmospheric data')

wave class-attribute instance-attribute

wave: Optional[Union[DataBlob, SCHISMDataWave]] = Field(None, description='wave data')

boundary_conditions class-attribute instance-attribute

boundary_conditions: Optional[SCHISMDataBoundaryConditions] = Field(None, description='unified boundary conditions (replaces tides and ocean)')

Functions

get

get(destdir: str | Path, grid: SCHISMGrid, time: TimeRange) -> Dict[str, Any]

Process all SCHISM forcing data and generate necessary input files.

Parameters

destdir : str | Path Destination directory grid : SCHISMGrid SCHISM grid instance time : TimeRange Time range for the simulation

Returns

Dict[str, Any] Paths to generated files for each data component

Source code in rompy_schism/data.py
def get(
    self,
    destdir: str | Path,
    grid: SCHISMGrid,
    time: TimeRange,
) -> Dict[str, Any]:
    """
    Process all SCHISM forcing data and generate necessary input files.

    Parameters
    ----------
    destdir : str | Path
        Destination directory
    grid : SCHISMGrid
        SCHISM grid instance
    time : TimeRange
        Time range for the simulation

    Returns
    -------
    Dict[str, Any]
        Paths to generated files for each data component
    """
    from rompy.formatting import ARROW

    # Convert destdir to Path object
    destdir = Path(destdir)

    # Create destdir if it doesn't exist
    if not destdir.exists():
        destdir.mkdir(parents=True, exist_ok=True)

    results = {}

    # Process atmospheric data
    if self.atmos:
        logger.info(f"{ARROW} Processing atmospheric forcing data")
        results["atmos"] = self.atmos.get(destdir, grid, time)
        logger.info(f"{ARROW} Atmospheric data processed successfully")

    # Process wave data
    if self.wave:
        logger.info(f"{ARROW} Processing wave boundary data")
        # Get source information
        if hasattr(self.wave, "source") and hasattr(self.wave.source, "uri"):
            logger.info(f"  • Source: {self.wave.source.uri}")
        elif hasattr(self.wave, "source") and hasattr(
            self.wave.source, "catalog_uri"
        ):
            logger.info(
                f"  • Source: {self.wave.source.catalog_uri} (dataset: {getattr(self.wave.source, 'dataset_id', 'unknown')})"
            )

        results["wave"] = self.wave.get(destdir, grid, time)
        logger.info(f"  • Output: {results['wave']}")
        logger.info(f"{ARROW} Wave data processed successfully")

    # Process boundary conditions
    if self.boundary_conditions:
        logger.info(f"{ARROW} Processing boundary conditions")
        results["boundary_conditions"] = self.boundary_conditions.get(
            destdir, grid, time
        )
        logger.info(f"{ARROW} Boundary conditions processed successfully")

    return results

Boundary Conditions

The boundary conditions module provides a unified interface for configuring all types of SCHISM boundary conditions including tidal, ocean, river, and nested model boundaries.

Boundary Conditions

Hotstart Configuration

The hotstart system provides integrated initial condition file generation, allowing you to create hotstart.nc files from the same ocean data sources used for boundary conditions.

Hotstart

Backend Framework

The backend framework provides unified execution of SCHISM simulations using Docker containers with automatic image building and comprehensive testing capabilities.

Backend Framework Backend Tutorial

SCHISMDataBoundaryConditions

Bases: RompyBaseModel

This class configures all boundary conditions for SCHISM including tidal, ocean, river, and nested model boundaries.

It provides a unified interface for specifying boundary conditions and their data sources, replacing the separate tides and ocean configurations.

Source code in rompy_schism/data.py
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
1817
1818
1819
1820
1821
1822
1823
1824
1825
1826
1827
1828
1829
1830
1831
1832
1833
1834
1835
1836
1837
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1850
1851
1852
1853
1854
1855
1856
1857
1858
1859
1860
1861
1862
1863
1864
class SCHISMDataBoundaryConditions(RompyBaseModel):
    """
    This class configures all boundary conditions for SCHISM including tidal,
    ocean, river, and nested model boundaries.

    It provides a unified interface for specifying boundary conditions and their
    data sources, replacing the separate tides and ocean configurations.
    """

    # Allow arbitrary types for schema generation
    model_config = ConfigDict(arbitrary_types_allowed=True)

    data_type: Literal["boundary_conditions"] = Field(
        default="boundary_conditions",
        description="Model type discriminator",
    )

    # Tidal dataset specification
    tidal_data: Optional[TidalDataset] = Field(
        None,
        description="Tidal forcing dataset",
    )

    # Boundary configurations with integrated data sources
    boundaries: Dict[int, BoundarySetupWithSource] = Field(
        default_factory=dict,
        description="Boundary configuration by boundary index",
    )

    # Predefined configuration types
    setup_type: Optional[Literal["tidal", "hybrid", "river", "nested"]] = Field(
        None, description="Predefined boundary setup type"
    )

    # Hotstart configuration
    hotstart_config: Optional[HotstartConfig] = Field(
        None, description="Configuration for hotstart file generation"
    )

    @model_validator(mode="before")
    @classmethod
    def convert_numpy_types(cls, data):
        """Convert any numpy values to Python native types"""
        if not isinstance(data, dict):
            return data

        for key, value in list(data.items()):
            if isinstance(value, (np.bool_, np.integer, np.floating, np.ndarray)):
                data[key] = to_python_type(value)
        return data

    @model_validator(mode="after")
    def validate_tidal_data(self):
        """Ensure tidal data is provided when needed for TIDAL or TIDALSPACETIME boundaries."""
        boundaries = self.boundaries or {}
        needs_tidal_data = False

        # Check setup_type first
        if self.setup_type in ["tidal", "hybrid"]:
            needs_tidal_data = True

        # Then check individual boundaries
        for setup in boundaries.values():
            if (
                hasattr(setup, "elev_type")
                and setup.elev_type
                in [ElevationType.HARMONIC, ElevationType.HARMONICEXTERNAL]
            ) or (
                hasattr(setup, "vel_type")
                and setup.vel_type
                in [VelocityType.HARMONIC, VelocityType.HARMONICEXTERNAL]
            ):
                needs_tidal_data = True
                break

        if needs_tidal_data and not self.tidal_data:
            raise ValueError(
                "Tidal data is required for HARMONIC or HARMONICEXTERNAL boundary types but was not provided"
            )

        return self

    @model_validator(mode="after")
    def validate_setup_type(self):
        """Validate setup type specific requirements."""
        # Skip validation if setup_type is not set
        if not self.setup_type:
            return self

        if self.setup_type in ["tidal", "hybrid"]:
            if not self.tidal_data:
                raise ValueError(
                    "tidal_data is required for tidal or hybrid setup_type"
                )

        elif self.setup_type == "river":
            if self.boundaries:
                has_flow = any(
                    hasattr(s, "const_flow") and s.const_flow is not None
                    for s in self.boundaries.values()
                )
                if not has_flow:
                    raise ValueError(
                        "At least one boundary should have const_flow for river setup_type"
                    )

        elif self.setup_type == "nested":
            if self.boundaries:
                for idx, setup in self.boundaries.items():
                    if (
                        hasattr(setup, "vel_type")
                        and setup.vel_type == VelocityType.RELAXED
                    ):
                        if not hasattr(setup, "inflow_relax") or not hasattr(
                            setup, "outflow_relax"
                        ):
                            logger.warning(
                                f"inflow_relax and outflow_relax are recommended for nested setup_type in boundary {idx}"
                            )
        else:
            raise ValueError(
                f"Unknown setup_type: {self.setup_type}. Expected one of: tidal, hybrid, river, nested"
            )

        return self

    def _create_boundary_config(self, grid):
        """Create a TidalBoundary object based on the configuration."""
        # Get tidal data paths
        if self.tidal_data:
            if (
                hasattr(self.tidal_data, "tidal_database")
                and self.tidal_data.tidal_database
            ):
                str(self.tidal_data.tidal_database)

        # Ensure boundary information is computed
        if hasattr(grid.pylibs_hgrid, "compute_bnd"):
            grid.pylibs_hgrid.compute_bnd()
        else:
            logger.warning(
                "Grid object doesn't have compute_bnd method. Boundary information may be missing."
            )

        # Create a new TidalBoundary with all the configuration
        # Ensure boundary information is computed before creating the boundary
        if not hasattr(grid.pylibs_hgrid, "nob") or not hasattr(
            grid.pylibs_hgrid, "nobn"
        ):
            logger.info("Computing boundary information before creating TidalBoundary")
            # First try compute_bnd if available
            if hasattr(grid.pylibs_hgrid, "compute_bnd"):
                grid.pylibs_hgrid.compute_bnd()

            # Then try compute_all if nob is still missing
            if not hasattr(grid.pylibs_hgrid, "nob") and hasattr(
                grid.pylibs_hgrid, "compute_all"
            ):
                if hasattr(grid.pylibs_hgrid, "compute_all"):
                    grid.pylibs_hgrid.compute_all()

        # Verify boundary attributes are available
        if not hasattr(grid.pylibs_hgrid, "nob"):
            logger.error("Failed to set 'nob' attribute on grid.pylibs_hgrid")
            raise AttributeError(
                "Missing required 'nob' attribute on grid.pylibs_hgrid"
            )

        # Create TidalBoundary with pre-computed grid to avoid losing boundary info
        # Get the grid path for TidalBoundary
        grid_path = (
            str(grid.hgrid.path)
            if hasattr(grid, "hgrid") and hasattr(grid.hgrid, "path")
            else None
        )
        if grid_path is None:
            # Create a temporary file with the grid if needed
            import tempfile

            temp_file = tempfile.NamedTemporaryFile(suffix=".gr3", delete=False)
            temp_path = temp_file.name
            temp_file.close()
            grid.pylibs_hgrid.write_hgrid(temp_path)
            grid_path = temp_path

        boundary = BoundaryHandler(grid_path=grid_path, tidal_data=self.tidal_data)

        # Replace the TidalBoundary's grid with our pre-computed one to preserve boundary info
        boundary.grid = grid.pylibs_hgrid

        # Configure each boundary segment
        for idx, setup in self.boundaries.items():
            boundary_config = setup.to_boundary_config()
            boundary.set_boundary_config(idx, boundary_config)

        return boundary

    def get(
        self,
        destdir: str | Path,
        grid: SCHISMGrid,
        time: TimeRange,
    ) -> Dict[str, str]:
        """
        Process all boundary data and generate necessary input files.

        Parameters
        ----------
        destdir : str | Path
            Destination directory
        grid : SCHISMGrid
            SCHISM grid instance
        time : TimeRange
            Time range for the simulation

        Returns
        -------
        Dict[str, str]
            Paths to generated files
        """
        # Processing boundary conditions

        # Convert destdir to Path object
        destdir = Path(destdir)

        # Create destdir if it doesn't exist
        if not destdir.exists():
            logger.info(f"Creating destination directory: {destdir}")
            destdir.mkdir(parents=True, exist_ok=True)

        # # 1. Process tidal data if needed
        if self.tidal_data:
            logger.info(
                f"{ARROW} Processing tidal constituents: {', '.join(self.tidal_data.constituents) if hasattr(self.tidal_data, 'constituents') else 'default'}"
            )
            self.tidal_data.get(grid)

        # 2. Create boundary condition file (bctides.in)
        boundary = self._create_boundary_config(grid)

        # Set start time and run duration
        start_time = time.start
        if time.end is not None and time.start is not None:
            run_days = (
                time.end - time.start
            ).total_seconds() / 86400.0  # Convert to days
        else:
            run_days = 1.0  # Default to 1 day if time is not properly specified
        boundary.set_run_parameters(start_time, run_days)

        # Generate bctides.in file
        bctides_path = destdir / "bctides.in"
        logger.info(f"{ARROW} Generating boundary condition file: bctides.in")

        # Ensure grid object has complete boundary information before writing
        if hasattr(grid.pylibs_hgrid, "compute_all"):
            grid.pylibs_hgrid.compute_all()

        # Double-check all required attributes are present
        required_attrs = ["nob", "nobn", "iobn"]
        missing_attrs = [
            attr
            for attr in required_attrs
            if not (grid.pylibs_hgrid and hasattr(grid.pylibs_hgrid, attr))
        ]
        if missing_attrs:
            error_msg = (
                f"Grid is missing required attributes: {', '.join(missing_attrs)}"
            )
            logger.error(error_msg)
            raise AttributeError(error_msg)

        # Write the boundary file - no fallbacks
        boundary.write_boundary_file(bctides_path)
        logger.info(f"{ARROW} Boundary conditions written successfully")

        # 3. Process ocean data based on boundary configurations
        processed_files = {"bctides": str(bctides_path)}

        # Collect variables to process and source information for logging
        variables_to_process = []
        source_files = set()
        for idx, setup in self.boundaries.items():
            if (
                setup.elev_type
                in [ElevationType.EXTERNAL, ElevationType.HARMONICEXTERNAL]
                and setup.elev_source
            ):
                variables_to_process.append("elevation")
                if hasattr(setup.elev_source, "source") and hasattr(
                    setup.elev_source.source, "uri"
                ):
                    source_files.add(str(setup.elev_source.source.uri))
            if (
                setup.vel_type
                in [
                    VelocityType.EXTERNAL,
                    VelocityType.HARMONICEXTERNAL,
                    VelocityType.RELAXED,
                ]
                and setup.vel_source
            ):
                variables_to_process.append("velocity")
                if hasattr(setup.vel_source, "source") and hasattr(
                    setup.vel_source.source, "uri"
                ):
                    source_files.add(str(setup.vel_source.source.uri))
            if setup.temp_type == TracerType.EXTERNAL and setup.temp_source:
                variables_to_process.append("temperature")
                if hasattr(setup.temp_source, "source") and hasattr(
                    setup.temp_source.source, "uri"
                ):
                    source_files.add(str(setup.temp_source.source.uri))
            if setup.salt_type == TracerType.EXTERNAL and setup.salt_source:
                variables_to_process.append("salinity")
                if hasattr(setup.salt_source, "source") and hasattr(
                    setup.salt_source.source, "uri"
                ):
                    source_files.add(str(setup.salt_source.source.uri))

        if variables_to_process:
            unique_vars = list(
                dict.fromkeys(variables_to_process)
            )  # Remove duplicates while preserving order
            logger.info(f"{ARROW} Processing boundary data: {', '.join(unique_vars)}")
            if source_files:
                if len(source_files) == 1:
                    logger.info(f"  • Source: {list(source_files)[0]}")
                else:
                    logger.info(f"  • Sources: {len(source_files)} files")

        # Process each data source based on the boundary type
        for idx, setup in self.boundaries.items():
            # Process elevation data if needed
            if setup.elev_type in [
                ElevationType.EXTERNAL,
                ElevationType.HARMONICEXTERNAL,
            ]:
                if setup.elev_source:
                    if (
                        hasattr(setup.elev_source, "data_type")
                        and setup.elev_source.data_type == "boundary"
                    ):
                        # Process using SCHISMDataBoundary interface
                        setup.elev_source.id = "elev2D"  # Set the ID for the boundary
                        file_path = setup.elev_source.get(destdir, grid, time)
                    else:
                        # Process using DataBlob interface
                        file_path = setup.elev_source.get(str(destdir))
                    processed_files[f"elev_boundary_{idx}"] = file_path

            # Process velocity data if needed
            if setup.vel_type in [
                VelocityType.EXTERNAL,
                VelocityType.HARMONICEXTERNAL,
                VelocityType.RELAXED,
            ]:
                if setup.vel_source:
                    if (
                        hasattr(setup.vel_source, "data_type")
                        and setup.vel_source.data_type == "boundary"
                    ):
                        # Process using SCHISMDataBoundary interface
                        setup.vel_source.id = "uv3D"  # Set the ID for the boundary
                        file_path = setup.vel_source.get(destdir, grid, time)
                    else:
                        # Process using DataBlob interface
                        file_path = setup.vel_source.get(str(destdir))
                    processed_files[f"vel_boundary_{idx}"] = file_path

            # Process temperature data if needed
            if setup.temp_type == TracerType.EXTERNAL:
                if setup.temp_source:
                    if (
                        hasattr(setup.temp_source, "data_type")
                        and setup.temp_source.data_type == "boundary"
                    ):
                        # Process using SCHISMDataBoundary interface
                        setup.temp_source.id = "TEM_3D"  # Set the ID for the boundary
                        file_path = setup.temp_source.get(destdir, grid, time)
                    else:
                        # Process using DataBlob interface
                        file_path = setup.temp_source.get(str(destdir))
                    processed_files[f"temp_boundary_{idx}"] = file_path

            # Process salinity data if needed
            if setup.salt_type == TracerType.EXTERNAL:
                if setup.salt_source:
                    if (
                        hasattr(setup.salt_source, "data_type")
                        and setup.salt_source.data_type == "boundary"
                    ):
                        # Process using SCHISMDataBoundary interface
                        setup.salt_source.id = "SAL_3D"  # Set the ID for the boundary
                        file_path = setup.salt_source.get(destdir, grid, time)
                    else:
                        # Process using DataBlob interface
                        file_path = setup.salt_source.get(str(destdir))
                    processed_files[f"salt_boundary_{idx}"] = file_path

        # Generate hotstart file if configured
        if self.hotstart_config and self.hotstart_config.enabled:
            logger.info(f"{ARROW} Generating hotstart file")
            hotstart_path = self._generate_hotstart(destdir, grid, time)
            processed_files["hotstart"] = hotstart_path
            logger.info(f"  • Output: {hotstart_path}")

        # Log summary of processed files with more details
        boundary_data_files = [f for k, f in processed_files.items() if "boundary" in k]
        if boundary_data_files:
            logger.info(
                f"  • Files: {', '.join([Path(f).name for f in boundary_data_files])}"
            )

        return processed_files

    def _generate_hotstart(
        self,
        destdir: Union[str, Path],
        grid: SCHISMGrid,
        time: Optional[TimeRange] = None,
    ) -> str:
        """
        Generate hotstart file using boundary condition data sources.

        Args:
            destdir: Destination directory for the hotstart file
            grid: SCHISM grid object
            time: Time range for the data

        Returns:
            Path to the generated hotstart file
        """
        from rompy_schism.hotstart import SCHISMDataHotstart

        # Find a boundary that has both temperature and salinity sources
        temp_source = None
        salt_source = None

        for boundary_config in self.boundaries.values():
            if boundary_config.temp_source is not None:
                temp_source = boundary_config.temp_source
            if boundary_config.salt_source is not None:
                salt_source = boundary_config.salt_source

            # If we found both, we can proceed
            if temp_source is not None and salt_source is not None:
                break

        if temp_source is None or salt_source is None:
            raise ValueError(
                "Hotstart generation requires both temperature and salinity sources "
                "to be configured in boundary conditions"
            )

        # Create hotstart instance using the first available source
        # (assuming temp and salt sources point to the same dataset)
        # Include both temperature and salinity variables for hotstart generation
        temp_var_name = (
            self.hotstart_config.temp_var if self.hotstart_config else "temperature"
        )
        salt_var_name = (
            self.hotstart_config.salt_var if self.hotstart_config else "salinity"
        )

        # Log hotstart generation details
        logger.info(f"  • Variables: {temp_var_name}, {salt_var_name}")
        if hasattr(temp_source, "source") and hasattr(temp_source.source, "uri"):
            logger.info(f"  • Source: {temp_source.source.uri}")

        hotstart_data = SCHISMDataHotstart(
            source=temp_source.source,
            variables=[temp_var_name, salt_var_name],
            coords=getattr(temp_source, "coords", None),
            temp_var=temp_var_name,
            salt_var=salt_var_name,
            time_offset=(
                self.hotstart_config.time_offset if self.hotstart_config else 0.0
            ),
            time_base=(
                self.hotstart_config.time_base
                if self.hotstart_config
                else datetime(2000, 1, 1)
            ),
            output_filename=(
                self.hotstart_config.output_filename
                if self.hotstart_config
                else "hotstart.nc"
            ),
        )

        return hotstart_data.get(str(destdir), grid=grid, time=time)

Attributes

model_config class-attribute instance-attribute

model_config = ConfigDict(arbitrary_types_allowed=True)

data_type class-attribute instance-attribute

data_type: Literal['boundary_conditions'] = Field(default='boundary_conditions', description='Model type discriminator')

tidal_data class-attribute instance-attribute

tidal_data: Optional[TidalDataset] = Field(None, description='Tidal forcing dataset')

boundaries class-attribute instance-attribute

boundaries: Dict[int, BoundarySetupWithSource] = Field(default_factory=dict, description='Boundary configuration by boundary index')

setup_type class-attribute instance-attribute

setup_type: Optional[Literal['tidal', 'hybrid', 'river', 'nested']] = Field(None, description='Predefined boundary setup type')

hotstart_config class-attribute instance-attribute

hotstart_config: Optional[HotstartConfig] = Field(None, description='Configuration for hotstart file generation')

Functions

convert_numpy_types classmethod

convert_numpy_types(data)

Convert any numpy values to Python native types

Source code in rompy_schism/data.py
@model_validator(mode="before")
@classmethod
def convert_numpy_types(cls, data):
    """Convert any numpy values to Python native types"""
    if not isinstance(data, dict):
        return data

    for key, value in list(data.items()):
        if isinstance(value, (np.bool_, np.integer, np.floating, np.ndarray)):
            data[key] = to_python_type(value)
    return data

validate_tidal_data

validate_tidal_data()

Ensure tidal data is provided when needed for TIDAL or TIDALSPACETIME boundaries.

Source code in rompy_schism/data.py
@model_validator(mode="after")
def validate_tidal_data(self):
    """Ensure tidal data is provided when needed for TIDAL or TIDALSPACETIME boundaries."""
    boundaries = self.boundaries or {}
    needs_tidal_data = False

    # Check setup_type first
    if self.setup_type in ["tidal", "hybrid"]:
        needs_tidal_data = True

    # Then check individual boundaries
    for setup in boundaries.values():
        if (
            hasattr(setup, "elev_type")
            and setup.elev_type
            in [ElevationType.HARMONIC, ElevationType.HARMONICEXTERNAL]
        ) or (
            hasattr(setup, "vel_type")
            and setup.vel_type
            in [VelocityType.HARMONIC, VelocityType.HARMONICEXTERNAL]
        ):
            needs_tidal_data = True
            break

    if needs_tidal_data and not self.tidal_data:
        raise ValueError(
            "Tidal data is required for HARMONIC or HARMONICEXTERNAL boundary types but was not provided"
        )

    return self

validate_setup_type

validate_setup_type()

Validate setup type specific requirements.

Source code in rompy_schism/data.py
@model_validator(mode="after")
def validate_setup_type(self):
    """Validate setup type specific requirements."""
    # Skip validation if setup_type is not set
    if not self.setup_type:
        return self

    if self.setup_type in ["tidal", "hybrid"]:
        if not self.tidal_data:
            raise ValueError(
                "tidal_data is required for tidal or hybrid setup_type"
            )

    elif self.setup_type == "river":
        if self.boundaries:
            has_flow = any(
                hasattr(s, "const_flow") and s.const_flow is not None
                for s in self.boundaries.values()
            )
            if not has_flow:
                raise ValueError(
                    "At least one boundary should have const_flow for river setup_type"
                )

    elif self.setup_type == "nested":
        if self.boundaries:
            for idx, setup in self.boundaries.items():
                if (
                    hasattr(setup, "vel_type")
                    and setup.vel_type == VelocityType.RELAXED
                ):
                    if not hasattr(setup, "inflow_relax") or not hasattr(
                        setup, "outflow_relax"
                    ):
                        logger.warning(
                            f"inflow_relax and outflow_relax are recommended for nested setup_type in boundary {idx}"
                        )
    else:
        raise ValueError(
            f"Unknown setup_type: {self.setup_type}. Expected one of: tidal, hybrid, river, nested"
        )

    return self

get

get(destdir: str | Path, grid: SCHISMGrid, time: TimeRange) -> Dict[str, str]

Process all boundary data and generate necessary input files.

Parameters

destdir : str | Path Destination directory grid : SCHISMGrid SCHISM grid instance time : TimeRange Time range for the simulation

Returns

Dict[str, str] Paths to generated files

Source code in rompy_schism/data.py
def get(
    self,
    destdir: str | Path,
    grid: SCHISMGrid,
    time: TimeRange,
) -> Dict[str, str]:
    """
    Process all boundary data and generate necessary input files.

    Parameters
    ----------
    destdir : str | Path
        Destination directory
    grid : SCHISMGrid
        SCHISM grid instance
    time : TimeRange
        Time range for the simulation

    Returns
    -------
    Dict[str, str]
        Paths to generated files
    """
    # Processing boundary conditions

    # Convert destdir to Path object
    destdir = Path(destdir)

    # Create destdir if it doesn't exist
    if not destdir.exists():
        logger.info(f"Creating destination directory: {destdir}")
        destdir.mkdir(parents=True, exist_ok=True)

    # # 1. Process tidal data if needed
    if self.tidal_data:
        logger.info(
            f"{ARROW} Processing tidal constituents: {', '.join(self.tidal_data.constituents) if hasattr(self.tidal_data, 'constituents') else 'default'}"
        )
        self.tidal_data.get(grid)

    # 2. Create boundary condition file (bctides.in)
    boundary = self._create_boundary_config(grid)

    # Set start time and run duration
    start_time = time.start
    if time.end is not None and time.start is not None:
        run_days = (
            time.end - time.start
        ).total_seconds() / 86400.0  # Convert to days
    else:
        run_days = 1.0  # Default to 1 day if time is not properly specified
    boundary.set_run_parameters(start_time, run_days)

    # Generate bctides.in file
    bctides_path = destdir / "bctides.in"
    logger.info(f"{ARROW} Generating boundary condition file: bctides.in")

    # Ensure grid object has complete boundary information before writing
    if hasattr(grid.pylibs_hgrid, "compute_all"):
        grid.pylibs_hgrid.compute_all()

    # Double-check all required attributes are present
    required_attrs = ["nob", "nobn", "iobn"]
    missing_attrs = [
        attr
        for attr in required_attrs
        if not (grid.pylibs_hgrid and hasattr(grid.pylibs_hgrid, attr))
    ]
    if missing_attrs:
        error_msg = (
            f"Grid is missing required attributes: {', '.join(missing_attrs)}"
        )
        logger.error(error_msg)
        raise AttributeError(error_msg)

    # Write the boundary file - no fallbacks
    boundary.write_boundary_file(bctides_path)
    logger.info(f"{ARROW} Boundary conditions written successfully")

    # 3. Process ocean data based on boundary configurations
    processed_files = {"bctides": str(bctides_path)}

    # Collect variables to process and source information for logging
    variables_to_process = []
    source_files = set()
    for idx, setup in self.boundaries.items():
        if (
            setup.elev_type
            in [ElevationType.EXTERNAL, ElevationType.HARMONICEXTERNAL]
            and setup.elev_source
        ):
            variables_to_process.append("elevation")
            if hasattr(setup.elev_source, "source") and hasattr(
                setup.elev_source.source, "uri"
            ):
                source_files.add(str(setup.elev_source.source.uri))
        if (
            setup.vel_type
            in [
                VelocityType.EXTERNAL,
                VelocityType.HARMONICEXTERNAL,
                VelocityType.RELAXED,
            ]
            and setup.vel_source
        ):
            variables_to_process.append("velocity")
            if hasattr(setup.vel_source, "source") and hasattr(
                setup.vel_source.source, "uri"
            ):
                source_files.add(str(setup.vel_source.source.uri))
        if setup.temp_type == TracerType.EXTERNAL and setup.temp_source:
            variables_to_process.append("temperature")
            if hasattr(setup.temp_source, "source") and hasattr(
                setup.temp_source.source, "uri"
            ):
                source_files.add(str(setup.temp_source.source.uri))
        if setup.salt_type == TracerType.EXTERNAL and setup.salt_source:
            variables_to_process.append("salinity")
            if hasattr(setup.salt_source, "source") and hasattr(
                setup.salt_source.source, "uri"
            ):
                source_files.add(str(setup.salt_source.source.uri))

    if variables_to_process:
        unique_vars = list(
            dict.fromkeys(variables_to_process)
        )  # Remove duplicates while preserving order
        logger.info(f"{ARROW} Processing boundary data: {', '.join(unique_vars)}")
        if source_files:
            if len(source_files) == 1:
                logger.info(f"  • Source: {list(source_files)[0]}")
            else:
                logger.info(f"  • Sources: {len(source_files)} files")

    # Process each data source based on the boundary type
    for idx, setup in self.boundaries.items():
        # Process elevation data if needed
        if setup.elev_type in [
            ElevationType.EXTERNAL,
            ElevationType.HARMONICEXTERNAL,
        ]:
            if setup.elev_source:
                if (
                    hasattr(setup.elev_source, "data_type")
                    and setup.elev_source.data_type == "boundary"
                ):
                    # Process using SCHISMDataBoundary interface
                    setup.elev_source.id = "elev2D"  # Set the ID for the boundary
                    file_path = setup.elev_source.get(destdir, grid, time)
                else:
                    # Process using DataBlob interface
                    file_path = setup.elev_source.get(str(destdir))
                processed_files[f"elev_boundary_{idx}"] = file_path

        # Process velocity data if needed
        if setup.vel_type in [
            VelocityType.EXTERNAL,
            VelocityType.HARMONICEXTERNAL,
            VelocityType.RELAXED,
        ]:
            if setup.vel_source:
                if (
                    hasattr(setup.vel_source, "data_type")
                    and setup.vel_source.data_type == "boundary"
                ):
                    # Process using SCHISMDataBoundary interface
                    setup.vel_source.id = "uv3D"  # Set the ID for the boundary
                    file_path = setup.vel_source.get(destdir, grid, time)
                else:
                    # Process using DataBlob interface
                    file_path = setup.vel_source.get(str(destdir))
                processed_files[f"vel_boundary_{idx}"] = file_path

        # Process temperature data if needed
        if setup.temp_type == TracerType.EXTERNAL:
            if setup.temp_source:
                if (
                    hasattr(setup.temp_source, "data_type")
                    and setup.temp_source.data_type == "boundary"
                ):
                    # Process using SCHISMDataBoundary interface
                    setup.temp_source.id = "TEM_3D"  # Set the ID for the boundary
                    file_path = setup.temp_source.get(destdir, grid, time)
                else:
                    # Process using DataBlob interface
                    file_path = setup.temp_source.get(str(destdir))
                processed_files[f"temp_boundary_{idx}"] = file_path

        # Process salinity data if needed
        if setup.salt_type == TracerType.EXTERNAL:
            if setup.salt_source:
                if (
                    hasattr(setup.salt_source, "data_type")
                    and setup.salt_source.data_type == "boundary"
                ):
                    # Process using SCHISMDataBoundary interface
                    setup.salt_source.id = "SAL_3D"  # Set the ID for the boundary
                    file_path = setup.salt_source.get(destdir, grid, time)
                else:
                    # Process using DataBlob interface
                    file_path = setup.salt_source.get(str(destdir))
                processed_files[f"salt_boundary_{idx}"] = file_path

    # Generate hotstart file if configured
    if self.hotstart_config and self.hotstart_config.enabled:
        logger.info(f"{ARROW} Generating hotstart file")
        hotstart_path = self._generate_hotstart(destdir, grid, time)
        processed_files["hotstart"] = hotstart_path
        logger.info(f"  • Output: {hotstart_path}")

    # Log summary of processed files with more details
    boundary_data_files = [f for k, f in processed_files.items() if "boundary" in k]
    if boundary_data_files:
        logger.info(
            f"  • Files: {', '.join([Path(f).name for f in boundary_data_files])}"
        )

    return processed_files

BoundarySetupWithSource

Bases: BoundarySetup

Enhanced boundary setup that includes data sources.

This class extends BoundarySetup to provide a unified configuration for both boundary conditions and their data sources.

Source code in rompy_schism/data.py
class BoundarySetupWithSource(BoundarySetup):
    """
    Enhanced boundary setup that includes data sources.

    This class extends BoundarySetup to provide a unified configuration
    for both boundary conditions and their data sources.
    """

    elev_source: Optional[Union[DataBlob, DataGrid, SCHISMDataBoundary]] = Field(
        None, description="Data source for elevation boundary condition"
    )
    vel_source: Optional[Union[DataBlob, DataGrid, SCHISMDataBoundary]] = Field(
        None, description="Data source for velocity boundary condition"
    )
    temp_source: Optional[Union[DataBlob, DataGrid, SCHISMDataBoundary]] = Field(
        None, description="Data source for temperature boundary condition"
    )
    salt_source: Optional[Union[DataBlob, DataGrid, SCHISMDataBoundary]] = Field(
        None, description="Data source for salinity boundary condition"
    )

    @model_validator(mode="after")
    def validate_data_sources(self):
        """Ensure data sources are provided when needed for space-time boundary types."""
        # Check elevation data source
        if (
            self.elev_type in [ElevationType.EXTERNAL, ElevationType.HARMONICEXTERNAL]
            and self.elev_source is None
        ):
            logger.warning(
                "elev_source should be provided for EXTERNAL or HARMONICEXTERNAL elevation type"
            )

        # Check velocity data source
        if (
            self.vel_type
            in [
                VelocityType.EXTERNAL,
                VelocityType.HARMONICEXTERNAL,
                VelocityType.RELAXED,
            ]
            and self.vel_source is None
        ):
            logger.warning(
                "vel_source should be provided for EXTERNAL, HARMONICEXTERNAL, or RELAXED velocity type"
            )

        # Check temperature data source
        if self.temp_type == TracerType.EXTERNAL and self.temp_source is None:
            logger.warning(
                "temp_source should be provided for EXTERNAL temperature type"
            )

        # Check salinity data source
        if self.salt_type == TracerType.EXTERNAL and self.salt_source is None:
            logger.warning("salt_source should be provided for EXTERNAL salinity type")

        return self

Attributes

elev_source class-attribute instance-attribute

elev_source: Optional[Union[DataBlob, DataGrid, SCHISMDataBoundary]] = Field(None, description='Data source for elevation boundary condition')

vel_source class-attribute instance-attribute

vel_source: Optional[Union[DataBlob, DataGrid, SCHISMDataBoundary]] = Field(None, description='Data source for velocity boundary condition')

temp_source class-attribute instance-attribute

temp_source: Optional[Union[DataBlob, DataGrid, SCHISMDataBoundary]] = Field(None, description='Data source for temperature boundary condition')

salt_source class-attribute instance-attribute

salt_source: Optional[Union[DataBlob, DataGrid, SCHISMDataBoundary]] = Field(None, description='Data source for salinity boundary condition')

Functions

validate_data_sources

validate_data_sources()

Ensure data sources are provided when needed for space-time boundary types.

Source code in rompy_schism/data.py
@model_validator(mode="after")
def validate_data_sources(self):
    """Ensure data sources are provided when needed for space-time boundary types."""
    # Check elevation data source
    if (
        self.elev_type in [ElevationType.EXTERNAL, ElevationType.HARMONICEXTERNAL]
        and self.elev_source is None
    ):
        logger.warning(
            "elev_source should be provided for EXTERNAL or HARMONICEXTERNAL elevation type"
        )

    # Check velocity data source
    if (
        self.vel_type
        in [
            VelocityType.EXTERNAL,
            VelocityType.HARMONICEXTERNAL,
            VelocityType.RELAXED,
        ]
        and self.vel_source is None
    ):
        logger.warning(
            "vel_source should be provided for EXTERNAL, HARMONICEXTERNAL, or RELAXED velocity type"
        )

    # Check temperature data source
    if self.temp_type == TracerType.EXTERNAL and self.temp_source is None:
        logger.warning(
            "temp_source should be provided for EXTERNAL temperature type"
        )

    # Check salinity data source
    if self.salt_type == TracerType.EXTERNAL and self.salt_source is None:
        logger.warning("salt_source should be provided for EXTERNAL salinity type")

    return self

create_tidal_only_boundary_config

create_tidal_only_boundary_config(constituents: Union[str, List[str]] = 'major', tidal_database: Union[str, Path] = None, tidal_model: Optional[str] = 'FES2014', nodal_corrections: bool = True, tidal_potential: bool = True, cutoff_depth: float = 50.0, tide_interpolation_method: str = 'bilinear')

Create a configuration where all open boundaries are treated as tidal boundaries.

Parameters

constituents : str or list, optional Tidal constituents to include, by default "major" tidal_database : str or Path, optional Path to tidal database for pyTMD, by default None tidal_model : str, optional Tidal model to use, by default 'FES2014' nodal_corrections : bool, optional Whether to apply nodal corrections, by default True tidal_potential : bool, optional Whether to include tidal potential, by default True cutoff_depth : float, optional Depth threshold for tidal potential, by default 50.0 tide_interpolation_method : str, optional Method for tide interpolation, by default "bilinear"

Returns

SCHISMDataBoundaryConditions Configured boundary conditions

Source code in rompy_schism/boundary_core.py
def create_tidal_only_boundary_config(
    constituents: Union[str, List[str]] = "major",
    tidal_database: Union[str, Path] = None,
    tidal_model: Optional[str] = "FES2014",
    nodal_corrections: bool = True,
    tidal_potential: bool = True,
    cutoff_depth: float = 50.0,
    tide_interpolation_method: str = "bilinear",
):
    """
    Create a configuration where all open boundaries are treated as tidal boundaries.

    Parameters
    ----------
    constituents : str or list, optional
        Tidal constituents to include, by default "major"
    tidal_database : str or Path, optional
        Path to tidal database for pyTMD, by default None
    tidal_model : str, optional
        Tidal model to use, by default 'FES2014'
    nodal_corrections : bool, optional
        Whether to apply nodal corrections, by default True
    tidal_potential : bool, optional
        Whether to include tidal potential, by default True
    cutoff_depth : float, optional
        Depth threshold for tidal potential, by default 50.0
    tide_interpolation_method : str, optional
        Method for tide interpolation, by default "bilinear"

    Returns
    -------
    SCHISMDataBoundaryConditions
        Configured boundary conditions
    """
    from rompy_schism.data import SCHISMDataBoundaryConditions

    # Create tidal dataset
    tidal_data = TidalDataset(
        constituents=constituents,
        tidal_database=tidal_database,
        tidal_model=tidal_model,
        nodal_corrections=nodal_corrections,
        tidal_potential=tidal_potential,
        cutoff_depth=cutoff_depth,
        tide_interpolation_method=tide_interpolation_method,
    )

    # Create the config with tidal setup
    config = SCHISMDataBoundaryConditions(
        tidal_data=tidal_data,
        setup_type="tidal",
        boundaries={},
        hotstart_config=None,
    )

    return config

create_hybrid_boundary_config

create_hybrid_boundary_config(constituents: Union[str, List[str]] = 'major', tidal_database: Union[str, Path] = None, tidal_model: Optional[str] = 'FES2014', nodal_corrections: bool = True, tidal_potential: bool = True, cutoff_depth: float = 50.0, tide_interpolation_method: str = 'bilinear', elev_source: Optional[Union[Any, Any]] = None, vel_source: Optional[Union[Any, Any]] = None, temp_source: Optional[Union[Any, Any]] = None, salt_source: Optional[Union[Any, Any]] = None)

Create a configuration for hybrid harmonic + external data boundaries.

Parameters

constituents : str or list, optional Tidal constituents to include, by default "major" tidal_database : str or Path, optional Path to tidal database for pyTMD, by default None tidal_model : str, optional Tidal model to use, by default 'FES2014' nodal_corrections : bool, optional Whether to apply nodal corrections, by default True tidal_potential : bool, optional Whether to include tidal potential, by default True cutoff_depth : float, optional Depth threshold for tidal potential, by default 50.0 tide_interpolation_method : str, optional Method for tide interpolation, by default "bilinear" elev_source : Union[DataBlob, SCHISMDataBoundary], optional Data source for elevation vel_source : Union[DataBlob, SCHISMDataBoundary], optional Data source for velocity temp_source : Union[DataBlob, SCHISMDataBoundary], optional Data source for temperature salt_source : Union[DataBlob, SCHISMDataBoundary], optional Data source for salinity

Returns

SCHISMDataBoundaryConditions Configured boundary conditions

Source code in rompy_schism/boundary_core.py
def create_hybrid_boundary_config(
    constituents: Union[str, List[str]] = "major",
    tidal_database: Union[str, Path] = None,
    tidal_model: Optional[str] = "FES2014",
    nodal_corrections: bool = True,
    tidal_potential: bool = True,
    cutoff_depth: float = 50.0,
    tide_interpolation_method: str = "bilinear",
    elev_source: Optional[Union[Any, Any]] = None,
    vel_source: Optional[Union[Any, Any]] = None,
    temp_source: Optional[Union[Any, Any]] = None,
    salt_source: Optional[Union[Any, Any]] = None,
):
    """
    Create a configuration for hybrid harmonic + external data boundaries.

    Parameters
    ----------
    constituents : str or list, optional
        Tidal constituents to include, by default "major"
    tidal_database : str or Path, optional
        Path to tidal database for pyTMD, by default None
    tidal_model : str, optional
        Tidal model to use, by default 'FES2014'
    nodal_corrections : bool, optional
        Whether to apply nodal corrections, by default True
    tidal_potential : bool, optional
        Whether to include tidal potential, by default True
    cutoff_depth : float, optional
        Depth threshold for tidal potential, by default 50.0
    tide_interpolation_method : str, optional
        Method for tide interpolation, by default "bilinear"
    elev_source : Union[DataBlob, SCHISMDataBoundary], optional
        Data source for elevation
    vel_source : Union[DataBlob, SCHISMDataBoundary], optional
        Data source for velocity
    temp_source : Union[DataBlob, SCHISMDataBoundary], optional
        Data source for temperature
    salt_source : Union[DataBlob, SCHISMDataBoundary], optional
        Data source for salinity

    Returns
    -------
    SCHISMDataBoundaryConditions
        Configured boundary conditions
    """
    from rompy_schism.data import BoundarySetupWithSource, SCHISMDataBoundaryConditions
    from rompy_schism.tides_enhanced import TidalDataset

    # Create tidal dataset
    tidal_data = TidalDataset(
        constituents=constituents,
        tidal_database=tidal_database,
        tidal_model=tidal_model,
        nodal_corrections=nodal_corrections,
        tidal_potential=tidal_potential,
        cutoff_depth=cutoff_depth,
        tide_interpolation_method=tide_interpolation_method,
    )

    # Create the config with hybrid setup
    config = SCHISMDataBoundaryConditions(
        tidal_data=tidal_data,
        setup_type="hybrid",
        boundaries={
            0: BoundarySetupWithSource(
                elev_type=ElevationType.HARMONICEXTERNAL,
                vel_type=VelocityType.HARMONICEXTERNAL
                if vel_source
                else VelocityType.NONE,
                temp_type=TracerType.EXTERNAL if temp_source else TracerType.INITIAL,
                salt_type=TracerType.EXTERNAL if salt_source else TracerType.INITIAL,
                elev_source=elev_source,
                vel_source=vel_source,
                temp_source=temp_source,
                salt_source=salt_source,
            )
        },
        hotstart_config=None,
    )

    return config

create_river_boundary_config

create_river_boundary_config(river_boundary_index: int = 0, river_flow: float = -100.0, other_boundaries: Literal['tidal', 'hybrid', 'none'] = 'tidal', constituents: Union[str, List[str]] = 'major', tidal_database: Union[str, Path] = None, tidal_model: Optional[str] = 'FES2014', nodal_corrections: bool = True, tidal_potential: bool = True, cutoff_depth: float = 50.0, tide_interpolation_method: str = 'bilinear')

Create a configuration with a designated river boundary and optional tidal boundaries.

Parameters

river_boundary_index : int Index of the river boundary river_flow : float Flow rate (negative for inflow) other_boundaries : str How to treat other boundaries ("tidal", "hybrid", or "none") constituents : str or list, optional Tidal constituents to include, by default "major" tidal_database : str or Path, optional Path to tidal database for pyTMD, by default None tidal_model : str, optional Tidal model to use, by default 'FES2014' nodal_corrections : bool, optional Whether to apply nodal corrections, by default True tidal_potential : bool, optional Whether to include tidal potential, by default True cutoff_depth : float, optional Depth threshold for tidal potential, by default 50.0 tide_interpolation_method : str, optional Method for tide interpolation, by default "bilinear"

Returns

SCHISMDataBoundaryConditions Configured boundary conditions

Source code in rompy_schism/boundary_core.py
def create_river_boundary_config(
    river_boundary_index: int = 0,
    river_flow: float = -100.0,  # Negative for inflow
    other_boundaries: Literal["tidal", "hybrid", "none"] = "tidal",
    constituents: Union[str, List[str]] = "major",
    tidal_database: Union[str, Path] = None,
    tidal_model: Optional[str] = "FES2014",
    nodal_corrections: bool = True,
    tidal_potential: bool = True,
    cutoff_depth: float = 50.0,
    tide_interpolation_method: str = "bilinear",
):
    """
    Create a configuration with a designated river boundary and optional tidal boundaries.

    Parameters
    ----------
    river_boundary_index : int
        Index of the river boundary
    river_flow : float
        Flow rate (negative for inflow)
    other_boundaries : str
        How to treat other boundaries ("tidal", "hybrid", or "none")
    constituents : str or list, optional
        Tidal constituents to include, by default "major"
    tidal_database : str or Path, optional
        Path to tidal database for pyTMD, by default None
    tidal_model : str, optional
        Tidal model to use, by default 'FES2014'
    nodal_corrections : bool, optional
        Whether to apply nodal corrections, by default True
    tidal_potential : bool, optional
        Whether to include tidal potential, by default True
    cutoff_depth : float, optional
        Depth threshold for tidal potential, by default 50.0
    tide_interpolation_method : str, optional
        Method for tide interpolation, by default "bilinear"

    Returns
    -------
    SCHISMDataBoundaryConditions
        Configured boundary conditions
    """
    from rompy_schism.data import BoundarySetupWithSource, SCHISMDataBoundaryConditions
    from rompy_schism.tides_enhanced import TidalDataset

    # Create tidal dataset if both paths are provided and needed
    tidal_data = None
    if other_boundaries in ["tidal", "hybrid"]:
        tidal_data = TidalDataset(
            constituents=constituents,
            tidal_database=tidal_database,
            tidal_model=tidal_model,
            nodal_corrections=nodal_corrections,
            tidal_potential=tidal_potential,
            cutoff_depth=cutoff_depth,
            tide_interpolation_method=tide_interpolation_method,
        )

    # Create the basic config
    config = SCHISMDataBoundaryConditions(
        tidal_data=tidal_data,
        setup_type="river",
        hotstart_config=None,
    )

    # Add the river boundary
    config.boundaries[river_boundary_index] = BoundarySetupWithSource(
        elev_type=ElevationType.NONE,
        vel_type=VelocityType.CONSTANT,
        temp_type=TracerType.NONE,
        salt_type=TracerType.NONE,
        const_flow=river_flow,
    )

    return config

create_nested_boundary_config

create_nested_boundary_config(with_tides: bool = True, inflow_relax: float = 0.8, outflow_relax: float = 0.2, elev_source: Optional[Union[Any, Any]] = None, vel_source: Optional[Union[Any, Any]] = None, temp_source: Optional[Union[Any, Any]] = None, salt_source: Optional[Union[Any, Any]] = None, constituents: Union[str, List[str]] = 'major', tidal_database: Union[str, Path] = None, tidal_model: Optional[str] = 'FES2014', nodal_corrections: bool = True, tidal_potential: bool = True, cutoff_depth: float = 50.0, tide_interpolation_method: str = 'bilinear')

Create a configuration for nested model boundaries with external data.

Parameters

with_tides : bool Include tidal components inflow_relax : float Relaxation parameter for inflow (0-1) outflow_relax : float Relaxation parameter for outflow (0-1) elev_source : Union[DataBlob, SCHISMDataBoundary], optional Data source for elevation vel_source : Union[DataBlob, SCHISMDataBoundary], optional Data source for velocity temp_source : Union[DataBlob, SCHISMDataBoundary], optional Data source for temperature salt_source : Union[DataBlob, SCHISMDataBoundary], optional Data source for salinity constituents : str or list, optional Tidal constituents to include, by default "major" tidal_database : str or Path, optional Path to tidal database for pyTMD, by default None tidal_model : str, optional Tidal model to use, by default 'FES2014' nodal_corrections : bool, optional Whether to apply nodal corrections, by default True tidal_potential : bool, optional Whether to include tidal potential, by default True cutoff_depth : float, optional Depth threshold for tidal potential, by default 50.0 tide_interpolation_method : str, optional Method for tide interpolation, by default "bilinear"

Returns

SCHISMDataBoundaryConditions Configured boundary conditions

Source code in rompy_schism/boundary_core.py
def create_nested_boundary_config(
    with_tides: bool = True,
    inflow_relax: float = 0.8,
    outflow_relax: float = 0.2,
    elev_source: Optional[Union[Any, Any]] = None,
    vel_source: Optional[Union[Any, Any]] = None,
    temp_source: Optional[Union[Any, Any]] = None,
    salt_source: Optional[Union[Any, Any]] = None,
    constituents: Union[str, List[str]] = "major",
    tidal_database: Union[str, Path] = None,
    tidal_model: Optional[str] = "FES2014",
    nodal_corrections: bool = True,
    tidal_potential: bool = True,
    cutoff_depth: float = 50.0,
    tide_interpolation_method: str = "bilinear",
):
    """
    Create a configuration for nested model boundaries with external data.

    Parameters
    ----------
    with_tides : bool
        Include tidal components
    inflow_relax : float
        Relaxation parameter for inflow (0-1)
    outflow_relax : float
        Relaxation parameter for outflow (0-1)
    elev_source : Union[DataBlob, SCHISMDataBoundary], optional
        Data source for elevation
    vel_source : Union[DataBlob, SCHISMDataBoundary], optional
        Data source for velocity
    temp_source : Union[DataBlob, SCHISMDataBoundary], optional
        Data source for temperature
    salt_source : Union[DataBlob, SCHISMDataBoundary], optional
        Data source for salinity
    constituents : str or list, optional
        Tidal constituents to include, by default "major"
    tidal_database : str or Path, optional
        Path to tidal database for pyTMD, by default None
    tidal_model : str, optional
        Tidal model to use, by default 'FES2014'
    nodal_corrections : bool, optional
        Whether to apply nodal corrections, by default True
    tidal_potential : bool, optional
        Whether to include tidal potential, by default True
    cutoff_depth : float, optional
        Depth threshold for tidal potential, by default 50.0
    tide_interpolation_method : str, optional
        Method for tide interpolation, by default "bilinear"

    Returns
    -------
    SCHISMDataBoundaryConditions
        Configured boundary conditions
    """
    from rompy_schism.data import BoundarySetupWithSource, SCHISMDataBoundaryConditions
    from rompy_schism.tides_enhanced import TidalDataset

    # Create tidal dataset if both paths are provided and needed
    tidal_data = None
    if with_tides:
        tidal_data = TidalDataset(
            constituents=constituents,
            tidal_database=tidal_database,
            tidal_model=tidal_model,
            nodal_corrections=nodal_corrections,
            tidal_potential=tidal_potential,
            cutoff_depth=cutoff_depth,
            tide_interpolation_method=tide_interpolation_method,
        )

    # Create the basic config
    config = SCHISMDataBoundaryConditions(
        tidal_data=tidal_data,
        setup_type="nested",
        hotstart_config=None,
    )

    # Determine elevation type based on tides setting
    elev_type = ElevationType.HARMONICEXTERNAL if with_tides else ElevationType.EXTERNAL

    # Add the nested boundary configuration
    config.boundaries[0] = BoundarySetupWithSource(
        elev_type=elev_type,
        vel_type=VelocityType.RELAXED,
        temp_type=TracerType.EXTERNAL if temp_source else TracerType.NONE,
        salt_type=TracerType.EXTERNAL if salt_source else TracerType.NONE,
        inflow_relax=inflow_relax,
        outflow_relax=outflow_relax,
        elev_source=elev_source,
        vel_source=vel_source,
        temp_source=temp_source,
        salt_source=salt_source,
    )

    return config

Config Minimal

This object has been implemented to meet the initial operational requirements of CSIRO. It is likely that this will be superceded by the full implementation.

SchismCSIROConfig

Bases: SchismCSIROConfig

Source code in rompy_schism/config.py
class SchismCSIROConfig(_LegacySchismCSIROConfig):
    def __init__(self, *args, **kwargs):
        warnings.warn(
            "The SchismCSIROMigrationConfig class from config.py is deprecated. "
        )
        super().__init__(*args, **kwargs)

Full Namelist Implementation

This object implements a set of models for each namelist and assembles a config object using this group of models. This is curently only partly implemented.

PARAM

Core

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/param.py
class Core(NamelistBaseModel):
    ipre: Optional[int] = Field(
        0,
        description="Pre-processor flag (1: on; 0: off). Useful for checking grid errors etc. Use 1 core only for compute (plus necessary scribe cores) when enabled. Under scribe I/O, the code (scribe part) will hang but outputs will be available. Job should be manually terminated.",
    )
    ibc: Optional[int] = Field(
        0,
        description="Baroclinic option flag. If set to 0 (baroclinic model), ibtp is not used.",
    )
    ibtp: Optional[int] = Field(
        1, description="Barotropic option flag. Only used when ibc is not 0."
    )
    rnday: Optional[float] = Field(30, description="Total run time in days.")
    dt: Optional[float] = Field(100.0, description="Time step in seconds.")
    msc2: Optional[int] = Field(
        24,
        description="Number of spectral frequencies for WWM grid. Must be the same as msc in .nml for consistency between SCHISM and WWM.",
    )
    mdc2: Optional[int] = Field(
        24,
        description="Number of directional bins for WWM grid. Must be the same as mdc in .nml for consistency between SCHISM and WWM.",
    )
    ntracer_gen: Optional[int] = Field(
        2, description="Number of tracers in user-defined module (USE_GEN)."
    )
    ntracer_age: Optional[int] = Field(
        4,
        description="Number of tracers for age calculation (USE_AGE). Must be equal to 2*N where N is the number of age tracers.",
    )
    sed_class: Optional[int] = Field(
        5, description="Number of sediment classes for SED3D module (USE_SED)."
    )
    eco_class: Optional[int] = Field(
        27,
        description="Number of ecological variables for EcoSim module (USE_ECO). Must be between 25 and 60, inclusive.",
    )
    nspool: Optional[int] = Field(
        36, description="Output step spool for global output controls."
    )
    ihfskip: Optional[int] = Field(
        864,
        description="Stack spool for global output controls. Every ihfskip steps will be put into 1_*, 2_*, etc.",
    )
    nbins_veg_vert: Optional[int] = Field(
        2,
        description="Number of vertical bins for vegetation model. Only used if iveg=1.",
    )

    @field_validator("ipre")
    @classmethod
    def validate_ipre(cls, v):
        if v not in [0, 1]:
            raise ValueError("ipre must be either 0 or 1")
        return v

    @field_validator("ibc")
    @classmethod
    def validate_ibc(cls, v):
        if v not in [0, 1]:
            raise ValueError("ibc must be either 0 or 1")
        return v

    @field_validator("ibtp")
    @classmethod
    def validate_ibtp(cls, v):
        if v not in [0, 1]:
            raise ValueError("ibtp must be either 0 or 1")
        return v

    @field_validator("rnday")
    @classmethod
    def validate_rnday(cls, v):
        if v <= 0:
            raise ValueError("rnday must be positive")
        return v

    @field_validator("dt")
    @classmethod
    def validate_dt(cls, v):
        if v <= 0:
            raise ValueError("dt must be positive")
        return v

    @field_validator("msc2")
    @classmethod
    def validate_msc2(cls, v):
        if v <= 0:
            raise ValueError("msc2 must be positive")
        return v

    @field_validator("mdc2")
    @classmethod
    def validate_mdc2(cls, v):
        if v <= 0:
            raise ValueError("mdc2 must be positive")
        return v

    @field_validator("ntracer_gen")
    @classmethod
    def validate_ntracer_gen(cls, v):
        if v < 0:
            raise ValueError("ntracer_gen must be non-negative")
        return v

    @field_validator("ntracer_age")
    @classmethod
    def validate_ntracer_age(cls, v):
        if v % 2 != 0 or v < 0:
            raise ValueError("ntracer_age must be a non-negative even number")
        return v

    @field_validator("sed_class")
    @classmethod
    def validate_sed_class(cls, v):
        if v < 0:
            raise ValueError("sed_class must be non-negative")
        return v

    @field_validator("eco_class")
    @classmethod
    def validate_eco_class(cls, v):
        if v < 25 or v > 60:
            raise ValueError("eco_class must be between 25 and 60")
        return v

    @field_validator("nspool")
    @classmethod
    def validate_nspool(cls, v):
        if v <= 0:
            raise ValueError("nspool must be positive")
        return v

    @field_validator("ihfskip")
    @classmethod
    def validate_ihfskip(cls, v):
        if v <= 0:
            raise ValueError("ihfskip must be positive")
        return v

    @model_validator(mode="after")
    def validate_ibc_ibtp(self):
        if self.ibc == 0 and self.ibtp != 1:
            raise ValueError("When ibc is 0, ibtp must be 1")
        return self

Attributes

ipre class-attribute instance-attribute

ipre: Optional[int] = Field(0, description='Pre-processor flag (1: on; 0: off). Useful for checking grid errors etc. Use 1 core only for compute (plus necessary scribe cores) when enabled. Under scribe I/O, the code (scribe part) will hang but outputs will be available. Job should be manually terminated.')

ibc class-attribute instance-attribute

ibc: Optional[int] = Field(0, description='Baroclinic option flag. If set to 0 (baroclinic model), ibtp is not used.')

ibtp class-attribute instance-attribute

ibtp: Optional[int] = Field(1, description='Barotropic option flag. Only used when ibc is not 0.')

rnday class-attribute instance-attribute

rnday: Optional[float] = Field(30, description='Total run time in days.')

dt class-attribute instance-attribute

dt: Optional[float] = Field(100.0, description='Time step in seconds.')

msc2 class-attribute instance-attribute

msc2: Optional[int] = Field(24, description='Number of spectral frequencies for WWM grid. Must be the same as msc in .nml for consistency between SCHISM and WWM.')

mdc2 class-attribute instance-attribute

mdc2: Optional[int] = Field(24, description='Number of directional bins for WWM grid. Must be the same as mdc in .nml for consistency between SCHISM and WWM.')

ntracer_gen class-attribute instance-attribute

ntracer_gen: Optional[int] = Field(2, description='Number of tracers in user-defined module (USE_GEN).')

ntracer_age class-attribute instance-attribute

ntracer_age: Optional[int] = Field(4, description='Number of tracers for age calculation (USE_AGE). Must be equal to 2*N where N is the number of age tracers.')

sed_class class-attribute instance-attribute

sed_class: Optional[int] = Field(5, description='Number of sediment classes for SED3D module (USE_SED).')

eco_class class-attribute instance-attribute

eco_class: Optional[int] = Field(27, description='Number of ecological variables for EcoSim module (USE_ECO). Must be between 25 and 60, inclusive.')

nspool class-attribute instance-attribute

nspool: Optional[int] = Field(36, description='Output step spool for global output controls.')

ihfskip class-attribute instance-attribute

ihfskip: Optional[int] = Field(864, description='Stack spool for global output controls. Every ihfskip steps will be put into 1_*, 2_*, etc.')

nbins_veg_vert class-attribute instance-attribute

nbins_veg_vert: Optional[int] = Field(2, description='Number of vertical bins for vegetation model. Only used if iveg=1.')

Functions

validate_ipre classmethod

validate_ipre(v)
Source code in rompy_schism/namelists/param.py
@field_validator("ipre")
@classmethod
def validate_ipre(cls, v):
    if v not in [0, 1]:
        raise ValueError("ipre must be either 0 or 1")
    return v

validate_ibc classmethod

validate_ibc(v)
Source code in rompy_schism/namelists/param.py
@field_validator("ibc")
@classmethod
def validate_ibc(cls, v):
    if v not in [0, 1]:
        raise ValueError("ibc must be either 0 or 1")
    return v

validate_ibtp classmethod

validate_ibtp(v)
Source code in rompy_schism/namelists/param.py
@field_validator("ibtp")
@classmethod
def validate_ibtp(cls, v):
    if v not in [0, 1]:
        raise ValueError("ibtp must be either 0 or 1")
    return v

validate_rnday classmethod

validate_rnday(v)
Source code in rompy_schism/namelists/param.py
@field_validator("rnday")
@classmethod
def validate_rnday(cls, v):
    if v <= 0:
        raise ValueError("rnday must be positive")
    return v

validate_dt classmethod

validate_dt(v)
Source code in rompy_schism/namelists/param.py
@field_validator("dt")
@classmethod
def validate_dt(cls, v):
    if v <= 0:
        raise ValueError("dt must be positive")
    return v

validate_msc2 classmethod

validate_msc2(v)
Source code in rompy_schism/namelists/param.py
@field_validator("msc2")
@classmethod
def validate_msc2(cls, v):
    if v <= 0:
        raise ValueError("msc2 must be positive")
    return v

validate_mdc2 classmethod

validate_mdc2(v)
Source code in rompy_schism/namelists/param.py
@field_validator("mdc2")
@classmethod
def validate_mdc2(cls, v):
    if v <= 0:
        raise ValueError("mdc2 must be positive")
    return v

validate_ntracer_gen classmethod

validate_ntracer_gen(v)
Source code in rompy_schism/namelists/param.py
@field_validator("ntracer_gen")
@classmethod
def validate_ntracer_gen(cls, v):
    if v < 0:
        raise ValueError("ntracer_gen must be non-negative")
    return v

validate_ntracer_age classmethod

validate_ntracer_age(v)
Source code in rompy_schism/namelists/param.py
@field_validator("ntracer_age")
@classmethod
def validate_ntracer_age(cls, v):
    if v % 2 != 0 or v < 0:
        raise ValueError("ntracer_age must be a non-negative even number")
    return v

validate_sed_class classmethod

validate_sed_class(v)
Source code in rompy_schism/namelists/param.py
@field_validator("sed_class")
@classmethod
def validate_sed_class(cls, v):
    if v < 0:
        raise ValueError("sed_class must be non-negative")
    return v

validate_eco_class classmethod

validate_eco_class(v)
Source code in rompy_schism/namelists/param.py
@field_validator("eco_class")
@classmethod
def validate_eco_class(cls, v):
    if v < 25 or v > 60:
        raise ValueError("eco_class must be between 25 and 60")
    return v

validate_nspool classmethod

validate_nspool(v)
Source code in rompy_schism/namelists/param.py
@field_validator("nspool")
@classmethod
def validate_nspool(cls, v):
    if v <= 0:
        raise ValueError("nspool must be positive")
    return v

validate_ihfskip classmethod

validate_ihfskip(v)
Source code in rompy_schism/namelists/param.py
@field_validator("ihfskip")
@classmethod
def validate_ihfskip(cls, v):
    if v <= 0:
        raise ValueError("ihfskip must be positive")
    return v

validate_ibc_ibtp

validate_ibc_ibtp()
Source code in rompy_schism/namelists/param.py
@model_validator(mode="after")
def validate_ibc_ibtp(self):
    if self.ibc == 0 and self.ibtp != 1:
        raise ValueError("When ibc is 0, ibtp must be 1")
    return self

Opt

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/param.py
class Opt(NamelistBaseModel):
    ipre2: Optional[int] = Field(
        0,
        description="Pre-processing flag for diagnostic outputs. If non-zero, the code will output drag coefficients (Cdp) and stop.",
    )
    itransport_only: Optional[int] = Field(
        0,
        description="Option to solve only tracer transport. 0: off, 1 or 2: on. When 2, additional variables are needed.",
    )
    iloadtide: Optional[int] = Field(
        0,
        description="Option to add self-attracting and loading tide (SAL) into tidal potential. 0: off, 1: needs loadtide_[FREQ].gr3 inputs, 2 or 3: simple scaling for gravity approach.",
    )
    loadtide_coef: Optional[float] = Field(
        0.1,
        description="Coefficient for SAL scaling. Used only if iloadtide is 2 or 3.",
    )
    start_year: Optional[int] = Field(
        2000, description="Starting year for the simulation."
    )
    start_month: Optional[int] = Field(
        1, description="Starting month for the simulation."
    )
    start_day: Optional[int] = Field(1, description="int")
    start_hour: Optional[int] = Field(0, description="double")
    utc_start: Optional[int] = Field(0, description="double")
    ics: Optional[int] = Field(2, description="Coordinate option")
    ihot: Optional[int] = Field(0, description="")
    ieos_type: Optional[int] = Field(0, description="")
    ieos_pres: Optional[int] = Field(
        0, description="used only if ieos_type=0. 0: without pressure effects"
    )
    eos_a: Optional[float] = Field(
        -0.1, description="needed if ieos_type=1; should be <=0"
    )
    eos_b: Optional[float] = Field(1001.0, description="needed if ieos_type=1")
    dramp: Optional[float] = Field(
        1.0, description="ramp-up period in days for b.c. etc (no ramp-up if <=0)"
    )
    drampbc: Optional[float] = Field(
        0.0, description="ramp-up period in days for baroclinic force"
    )
    iupwind_mom: Optional[int] = Field(0, description="")
    indvel: Optional[int] = Field(0, description="")
    ihorcon: Optional[int] = Field(0, description="")
    hvis_coef0: Optional[float] = Field(
        0.025,
        description="const. diffusion # if ihorcon/=0; <=0.025 for ihorcon=2, <=0.125 for ihorcon=1",
    )
    ishapiro: Optional[int] = Field(1, description="options")
    niter_shap: Optional[int] = Field(
        1, description="needed if ishapiro/=0: # of iterations with Shapiro filter"
    )
    shapiro0: Optional[float] = Field(0.5, description="needed only if ishapiro=1")
    thetai: Optional[float] = Field(0.6, description="")
    icou_elfe_wwm: Optional[int] = Field(0, description="")
    nstep_wwm: Optional[int] = Field(
        1, description="call WWM every this many time steps"
    )
    iwbl: Optional[int] = Field(
        0, description="wave boundary layer formulation (used only if USE_WMM and"
    )
    hmin_radstress: Optional[float] = Field(
        1.0,
        description="min. total water depth used only in radiation stress calculation [m]",
    )
    drampwafo: Optional[float] = Field(
        0.0,
        description="ramp-up period in days for the wave forces (no ramp-up if <=0)",
    )
    turbinj: Optional[float] = Field(
        0.15,
        description="% of depth-induced wave breaking energy injected in turbulence",
    )
    turbinjds: Optional[float] = Field(
        1.0,
        description="% of wave energy dissipated through whitecapping injected in turbulence",
    )
    alphaw: Optional[float] = Field(
        0.5,
        description="for itur=4 : scaling parameter for the surface roughness z0s = alphaw*Hm0.",
    )
    fwvor_advxy_stokes: Optional[int] = Field(
        1, description="--> Stokes drift advection (xy), Coriolis"
    )
    fwvor_advz_stokes: Optional[int] = Field(
        1, description="--> Stokes drift advection (z) , Coriolis"
    )
    fwvor_gradpress: Optional[int] = Field(1, description="--> Pressure term")
    fwvor_breaking: Optional[int] = Field(1, description="--> Wave breaking")
    fwvor_streaming: Optional[int] = Field(
        1, description="--> Wave streaming (works with iwbl /= 0)"
    )
    fwvor_wveg: Optional[int] = Field(
        0, description="--> Wave dissipation by vegetation acceleration term"
    )
    fwvor_wveg_nl: Optional[int] = Field(
        0,
        description="--> Non linear intrawave vegetation force (see Dean and Bender, 2006 or van Rooijen et al., 2016 for details)",
    )
    cur_wwm: Optional[int] = Field(0, description="Coupling current in WWM")
    wafo_obcramp: Optional[int] = Field(
        0, description="Ramp on wave forces at open boundary (1: on / 0: off)"
    )
    imm: Optional[int] = Field(0, description="")
    ibdef: Optional[int] = Field(
        10, description="needed if imm=1; # of steps used in deformation"
    )
    slam0: Optional[int] = Field(-124, description="lon - not really used")
    sfea0: Optional[int] = Field(45, description="lat")
    iunder_deep: Optional[int] = Field(0, description="")
    h1_bcc: Optional[float] = Field(50.0, description="[m]")
    h2_bcc: Optional[float] = Field(100.0, description="[m]; >h1_bcc")
    hw_depth: Optional[float] = Field(1000000.0, description="threshold depth in [m]")
    hw_ratio: Optional[float] = Field(0.5, description="ratio")
    ihydraulics: Optional[int] = Field(0, description="")
    if_source: Optional[int] = Field(0, description="")
    dramp_ss: Optional[int] = Field(
        2,
        description="needed if if_source/=0; ramp-up period in days for source/sinks (no ramp-up if <=0)",
    )
    # Removed in SCHISM 5.12
    # meth_sink: Optional[int] = Field(1, description="options to treat sinks @ dry elem")
    lev_tr_source__1: Optional[int] = Field(-9, description="T")
    lev_tr_source__2: Optional[int] = Field(-9, description="S")
    lev_tr_source__3: Optional[int] = Field(-9, description="GEN")
    lev_tr_source__4: Optional[int] = Field(
        -9, description="AGE: set -9999. in msource's AGE section"
    )
    lev_tr_source__5: Optional[int] = Field(-9, description="SED3D")
    lev_tr_source__6: Optional[int] = Field(-9, description="EcoSim")
    lev_tr_source__7: Optional[int] = Field(-9, description="ICM")
    lev_tr_source__8: Optional[int] = Field(-9, description="CoSINE")
    lev_tr_source__9: Optional[int] = Field(-9, description="Feco")
    lev_tr_source__10: Optional[int] = Field(-9, description="TIMOR")
    lev_tr_source__11: Optional[int] = Field(-9, description="FABM")
    lev_tr_source__12: Optional[int] = Field(-9, description="DVD")
    level_age: Optional[list] = Field(
        [9, -999], description="default: -999 (all levels)"
    )
    ihdif: Optional[int] = Field(0, description="")
    nchi: Optional[int] = Field(0, description="")
    dzb_min: Optional[float] = Field(
        0.5, description="needed if nchi=1; min. bottom boundary layer thickness [m]."
    )
    hmin_man: Optional[float] = Field(
        1.0, description="needed if nchi=-1: min. depth in Manning's formulation [m]"
    )
    ncor: Optional[int] = Field(1, description="should usually be 1 if ics=2")
    rlatitude: Optional[int] = Field(46, description="if ncor=-1")
    coricoef: Optional[int] = Field(0, description="if ncor=0")
    ic_elev: Optional[int] = Field(0, description="")
    nramp_elev: Optional[int] = Field(0, description="")
    inv_atm_bnd: Optional[int] = Field(0, description="0: off; 1: on")
    prmsl_ref: Optional[float] = Field(
        101325.0, description="reference atmos. pressure on bnd [Pa]"
    )
    flag_ic__1: Optional[int] = Field(0, description="T")
    flag_ic__2: Optional[int] = Field(0, description="S")
    flag_ic__3: Optional[int] = Field(1, description="GEN (user defined module)")
    flag_ic__5: Optional[int] = Field(1, description="SED3D")
    flag_ic__6: Optional[int] = Field(1, description="EcoSim")
    flag_ic__7: Optional[int] = Field(1, description="ICM")
    flag_ic__8: Optional[int] = Field(1, description="CoSINE")
    flag_ic__9: Optional[int] = Field(1, description="FIB")
    flag_ic__10: Optional[int] = Field(1, description="TIMOR")
    flag_ic__11: Optional[int] = Field(1, description="FABM")
    flag_ic__12: Optional[int] = Field(0, description="DVD (must=0)")
    gen_wsett: Optional[float] = Field(
        0, description="Settling vel [m/s] for GEN module"
    )
    ibcc_mean: Optional[int] = Field(0, description="")
    rmaxvel: Optional[float] = Field(5.0, description="")
    velmin_btrack: Optional[float] = Field(
        1e-4,
        description="min. vel for invoking btrack and for abnormal exit in quicksearch",
    )
    btrack_nudge: Optional[float] = Field(
        9.013e-3, description="Nudging factors for starting side/node"
    )
    ihhat: Optional[int] = Field(1, description="")
    inunfl: Optional[int] = Field(0, description="")
    h0: Optional[float] = Field(
        0.01, description="min. water depth for wetting/drying [m]"
    )
    shorewafo: Optional[int] = Field(0, description="Matters only if USE_WWM")
    moitn0: Optional[int] = Field(
        50, description="output spool for solver info; used only with JCG"
    )
    mxitn0: Optional[int] = Field(1500, description="max. iteration allowed")
    rtol0: Optional[float] = Field(1e-12, description="error tolerance")
    nadv: Optional[int] = Field(1, description="")
    dtb_max: Optional[float] = Field(30.0, description="in sec")
    dtb_min: Optional[float] = Field(10.0, description="")
    inter_mom: Optional[int] = Field(0, description="")
    kr_co: Optional[int] = Field(1, description="not used if inter_mom=0")
    itr_met: Optional[int] = Field(3, description="")
    h_tvd: Optional[float] = Field(5.0, description="cut-off depth (m)")
    eps1_tvd_imp: Optional[float] = Field(
        1e-4,
        description="suggested value is 1.e-4, but for large suspended load, need to use a smaller value (e.g. 1.e-9)",
    )
    eps2_tvd_imp: Optional[float] = Field(1e-14, description="")
    ielm_transport: Optional[int] = Field(0, description="1: turn on")
    max_subcyc: Optional[int] = Field(
        10,
        description="used only if ielm_transport/=0. Max # of subcycling per time step in transport allowed",
    )
    ip_weno: Optional[int] = Field(
        2,
        description="order of accuracy: 0- upwind; 1- linear polynomial, 2nd order; 2- quadratic polynomial, 3rd order",
    )
    courant_weno: Optional[float] = Field(
        0.5, description="Courant number for weno transport"
    )
    nquad: Optional[int] = Field(
        2, description="number of quad points on each side, nquad= 1 or 2"
    )
    ntd_weno: Optional[int] = Field(
        1,
        description="order of temporal discretization: (1) Euler (default); (3): 3rd-order Runge-Kutta (only for benchmarking)",
    )
    epsilon1: Optional[float] = Field(
        1e-3,
        description="coefficient for 2nd order weno smoother (larger values are more prone to numerical dispersion)",
    )
    epsilon2: Optional[float] = Field(
        1e-10,
        description="1st coefficient for 3rd order weno smoother (larger values are more prone to numerical dispersion",
    )
    i_prtnftl_weno: Optional[int] = Field(
        0,
        description="option for writing nonfatal errors on invalid temp. or salinity for density: (0) off; (1) on.",
    )
    epsilon3: Optional[float] = Field(
        1e-25,
        description="2nd coefficient for 3rd order weno smoother (inactive at the moment)",
    )
    ielad_weno: Optional[int] = Field(
        0,
        description="ielad, if ielad=1, use ELAD method to suppress dispersion (inactive at the moment)",
    )
    small_elad: Optional[float] = Field(
        1e-4, description="small (inactive at the moment)"
    )
    nws: Optional[int] = Field(
        0,
        description="""
        Atmos. option. Use nws=2 and USE_ATMOS for coupling with atmospheric model.
        If nws=0, no atmos. forcing is applied. If nws=1, atmos.
        variables are read in from wind.th. If nws=2, atmos. variables are
        read in from sflux_ files.
        If nws=4, ascii format is used for wind and atmos. pressure at each node (see source code).
        If nws=-1 (requires USE_PAHM), use Holland parametric wind model (barotropic only with wind and atmos. pressure).
        In this case, the Holland model is called every step so wtiminc is not used. An extra 
        input file is needed: hurricane-track.dat, in addition to a few parameters below.

        Stress calculation:
        If nws=2, ihconsv=1 and iwind_form=0, the stress is calculated from heat exchange
        routine; in this case USE_ATMOS cannot be on.
        Otherwise if iwind_form=-1, the stress is calculated from Pond & Pichard formulation;
        if iwind_form=1, Hwang (2018) formulation (Cd tapers off at high wind).
        If WWM is enabled and icou_elfe_wwm>0 and iwind_form=-2 or -3, stress is overwritten by WWM:
        If iwind_form=-2, stress=rho_air*ufric^2; scaled by rho_water
        If iwind_form=-3, the stress is calculated according to the param. of Donelan et al. (1993) based on the wave age.
        In all cases, if USE_ICE the stress in ice-covered portion is calculated by ICE routine.
        """,
    )
    wtiminc: Optional[float] = Field(
        150.0, description="time step for atmos. forcing. Default: same as dt"
    )
    drampwind: Optional[float] = Field(
        1.0, description="ramp-up period in days for wind (no ramp-up if <=0)"
    )
    iwindoff: Optional[int] = Field(
        0, description="needed only if nws/=0; '1': needs windfactor.gr3"
    )
    iwind_form: Optional[int] = Field(1, description="needed if nws/=0")
    model_type_pahm: Optional[int] = Field(
        10,
        description="only used if nws=-1: hurricane model type (1: Holland; 10: GAHM)",
    )
    ihconsv: Optional[int] = Field(0, description="heat exchange option")
    isconsv: Optional[int] = Field(0, description="evaporation/precipitation model")
    i_hmin_airsea_ex: Optional[int] = Field(2, description="no effect if ihconsv=0")
    hmin_airsea_ex: Optional[float] = Field(
        0.2, description="[m], no effect if ihconsv=0"
    )
    i_hmin_salt_ex: Optional[int] = Field(2, description="no effect if isconsv=0")
    hmin_salt_ex: Optional[float] = Field(
        0.2, description="[m], no effect if isconsv=0"
    )
    iprecip_off_bnd: Optional[int] = Field(
        0, description="if /=0, precip will be turned off near land bnd"
    )
    itur: Optional[int] = Field(3, description="Default: 0")
    dfv0: Optional[float] = Field(1e-2, description="needed if itur=0")
    dfh0: Optional[float] = Field(1e-4, description="needed if itur=0")
    mid: Optional[str] = Field("KL", description="needed if itur=3,5. Use KE if itur=5")
    stab: Optional[str] = Field(
        "KC",
        description="needed if itur=3 or 5. Use 'GA' if turb_met='MY'; otherwise use 'KC'.",
    )
    xlsc0: Optional[float] = Field(
        0.1,
        description="needed if itur=3 or 5. Scale for surface & bottom mixing length (>0)",
    )
    inu_elev: Optional[int] = Field(0, description="")
    inu_uv: Optional[int] = Field(0, description="")
    inu_tr__1: Optional[int] = Field(0, description="T")
    inu_tr__2: Optional[int] = Field(0, description="S")
    inu_tr__3: Optional[int] = Field(0, description="GEN")
    inu_tr__4: Optional[int] = Field(0, description="Age")
    inu_tr__5: Optional[int] = Field(0, description="SED3D")
    inu_tr__6: Optional[int] = Field(0, description="EcoSim")
    inu_tr__7: Optional[int] = Field(0, description="ICM")
    inu_tr__8: Optional[int] = Field(0, description="CoSINE")
    inu_tr__9: Optional[int] = Field(0, description="FIB")
    inu_tr__10: Optional[int] = Field(0, description="TIMOR")
    inu_tr__11: Optional[int] = Field(0, description="FABM")
    inu_tr__12: Optional[int] = Field(0, description="DVD (must=0)")
    nu_sum_mult: Optional[int] = Field(
        1, description="1: final relax is sum of horizontal"
    )

    @field_validator("ipre2")
    @classmethod
    def validate_ipre2(cls, v):
        if v not in [0, 1]:
            raise ValueError("ipre2 must be 0 or 1")
        return v

    @field_validator("itransport_only")
    @classmethod
    def validate_itransport_only(cls, v):
        if v not in [0, 1, 2]:
            raise ValueError("itransport_only must be 0, 1, or 2")
        return v

    @field_validator("iloadtide")
    @classmethod
    def validate_iloadtide(cls, v):
        if v not in [0, 1, 2, 3]:
            raise ValueError("iloadtide must be 0, 1, 2, or 3")
        return v

    @field_validator("loadtide_coef")
    @classmethod
    def validate_loadtide_coef(cls, v):
        if v < 0 or v > 1:
            raise ValueError("loadtide_coef must be between 0 and 1")
        return v

    @field_validator("start_year")
    @classmethod
    def validate_start_year(cls, v):
        if v < 1900 or v > 2100:
            raise ValueError("start_year must be between 1900 and 2100")
        return v

    @field_validator("start_month")
    @classmethod
    def validate_start_month(cls, v):
        if v < 1 or v > 12:
            raise ValueError("start_month must be between 1 and 12")
        return v

    @model_validator(mode="after")
    def check_loadtide_coef(self):
        if self.iloadtide in [2, 3] and self.loadtide_coef == 0:
            raise ValueError("loadtide_coef must be set when iloadtide is 2 or 3")
        return self

Attributes

ipre2 class-attribute instance-attribute

ipre2: Optional[int] = Field(0, description='Pre-processing flag for diagnostic outputs. If non-zero, the code will output drag coefficients (Cdp) and stop.')

itransport_only class-attribute instance-attribute

itransport_only: Optional[int] = Field(0, description='Option to solve only tracer transport. 0: off, 1 or 2: on. When 2, additional variables are needed.')

iloadtide class-attribute instance-attribute

iloadtide: Optional[int] = Field(0, description='Option to add self-attracting and loading tide (SAL) into tidal potential. 0: off, 1: needs loadtide_[FREQ].gr3 inputs, 2 or 3: simple scaling for gravity approach.')

loadtide_coef class-attribute instance-attribute

loadtide_coef: Optional[float] = Field(0.1, description='Coefficient for SAL scaling. Used only if iloadtide is 2 or 3.')

start_year class-attribute instance-attribute

start_year: Optional[int] = Field(2000, description='Starting year for the simulation.')

start_month class-attribute instance-attribute

start_month: Optional[int] = Field(1, description='Starting month for the simulation.')

start_day class-attribute instance-attribute

start_day: Optional[int] = Field(1, description='int')

start_hour class-attribute instance-attribute

start_hour: Optional[int] = Field(0, description='double')

utc_start class-attribute instance-attribute

utc_start: Optional[int] = Field(0, description='double')

ics class-attribute instance-attribute

ics: Optional[int] = Field(2, description='Coordinate option')

ihot class-attribute instance-attribute

ihot: Optional[int] = Field(0, description='')

ieos_type class-attribute instance-attribute

ieos_type: Optional[int] = Field(0, description='')

ieos_pres class-attribute instance-attribute

ieos_pres: Optional[int] = Field(0, description='used only if ieos_type=0. 0: without pressure effects')

eos_a class-attribute instance-attribute

eos_a: Optional[float] = Field(-0.1, description='needed if ieos_type=1; should be <=0')

eos_b class-attribute instance-attribute

eos_b: Optional[float] = Field(1001.0, description='needed if ieos_type=1')

dramp class-attribute instance-attribute

dramp: Optional[float] = Field(1.0, description='ramp-up period in days for b.c. etc (no ramp-up if <=0)')

drampbc class-attribute instance-attribute

drampbc: Optional[float] = Field(0.0, description='ramp-up period in days for baroclinic force')

iupwind_mom class-attribute instance-attribute

iupwind_mom: Optional[int] = Field(0, description='')

indvel class-attribute instance-attribute

indvel: Optional[int] = Field(0, description='')

ihorcon class-attribute instance-attribute

ihorcon: Optional[int] = Field(0, description='')

hvis_coef0 class-attribute instance-attribute

hvis_coef0: Optional[float] = Field(0.025, description='const. diffusion # if ihorcon/=0; <=0.025 for ihorcon=2, <=0.125 for ihorcon=1')

ishapiro class-attribute instance-attribute

ishapiro: Optional[int] = Field(1, description='options')

niter_shap class-attribute instance-attribute

niter_shap: Optional[int] = Field(1, description='needed if ishapiro/=0: # of iterations with Shapiro filter')

shapiro0 class-attribute instance-attribute

shapiro0: Optional[float] = Field(0.5, description='needed only if ishapiro=1')

thetai class-attribute instance-attribute

thetai: Optional[float] = Field(0.6, description='')

icou_elfe_wwm class-attribute instance-attribute

icou_elfe_wwm: Optional[int] = Field(0, description='')

nstep_wwm class-attribute instance-attribute

nstep_wwm: Optional[int] = Field(1, description='call WWM every this many time steps')

iwbl class-attribute instance-attribute

iwbl: Optional[int] = Field(0, description='wave boundary layer formulation (used only if USE_WMM and')

hmin_radstress class-attribute instance-attribute

hmin_radstress: Optional[float] = Field(1.0, description='min. total water depth used only in radiation stress calculation [m]')

drampwafo class-attribute instance-attribute

drampwafo: Optional[float] = Field(0.0, description='ramp-up period in days for the wave forces (no ramp-up if <=0)')

turbinj class-attribute instance-attribute

turbinj: Optional[float] = Field(0.15, description='% of depth-induced wave breaking energy injected in turbulence')

turbinjds class-attribute instance-attribute

turbinjds: Optional[float] = Field(1.0, description='% of wave energy dissipated through whitecapping injected in turbulence')

alphaw class-attribute instance-attribute

alphaw: Optional[float] = Field(0.5, description='for itur=4 : scaling parameter for the surface roughness z0s = alphaw*Hm0.')

fwvor_advxy_stokes class-attribute instance-attribute

fwvor_advxy_stokes: Optional[int] = Field(1, description='--> Stokes drift advection (xy), Coriolis')

fwvor_advz_stokes class-attribute instance-attribute

fwvor_advz_stokes: Optional[int] = Field(1, description='--> Stokes drift advection (z) , Coriolis')

fwvor_gradpress class-attribute instance-attribute

fwvor_gradpress: Optional[int] = Field(1, description='--> Pressure term')

fwvor_breaking class-attribute instance-attribute

fwvor_breaking: Optional[int] = Field(1, description='--> Wave breaking')

fwvor_streaming class-attribute instance-attribute

fwvor_streaming: Optional[int] = Field(1, description='--> Wave streaming (works with iwbl /= 0)')

fwvor_wveg class-attribute instance-attribute

fwvor_wveg: Optional[int] = Field(0, description='--> Wave dissipation by vegetation acceleration term')

fwvor_wveg_nl class-attribute instance-attribute

fwvor_wveg_nl: Optional[int] = Field(0, description='--> Non linear intrawave vegetation force (see Dean and Bender, 2006 or van Rooijen et al., 2016 for details)')

cur_wwm class-attribute instance-attribute

cur_wwm: Optional[int] = Field(0, description='Coupling current in WWM')

wafo_obcramp class-attribute instance-attribute

wafo_obcramp: Optional[int] = Field(0, description='Ramp on wave forces at open boundary (1: on / 0: off)')

imm class-attribute instance-attribute

imm: Optional[int] = Field(0, description='')

ibdef class-attribute instance-attribute

ibdef: Optional[int] = Field(10, description='needed if imm=1; # of steps used in deformation')

slam0 class-attribute instance-attribute

slam0: Optional[int] = Field(-124, description='lon - not really used')

sfea0 class-attribute instance-attribute

sfea0: Optional[int] = Field(45, description='lat')

iunder_deep class-attribute instance-attribute

iunder_deep: Optional[int] = Field(0, description='')

h1_bcc class-attribute instance-attribute

h1_bcc: Optional[float] = Field(50.0, description='[m]')

h2_bcc class-attribute instance-attribute

h2_bcc: Optional[float] = Field(100.0, description='[m]; >h1_bcc')

hw_depth class-attribute instance-attribute

hw_depth: Optional[float] = Field(1000000.0, description='threshold depth in [m]')

hw_ratio class-attribute instance-attribute

hw_ratio: Optional[float] = Field(0.5, description='ratio')

ihydraulics class-attribute instance-attribute

ihydraulics: Optional[int] = Field(0, description='')

if_source class-attribute instance-attribute

if_source: Optional[int] = Field(0, description='')

dramp_ss class-attribute instance-attribute

dramp_ss: Optional[int] = Field(2, description='needed if if_source/=0; ramp-up period in days for source/sinks (no ramp-up if <=0)')

lev_tr_source__1 class-attribute instance-attribute

lev_tr_source__1: Optional[int] = Field(-9, description='T')

lev_tr_source__2 class-attribute instance-attribute

lev_tr_source__2: Optional[int] = Field(-9, description='S')

lev_tr_source__3 class-attribute instance-attribute

lev_tr_source__3: Optional[int] = Field(-9, description='GEN')

lev_tr_source__4 class-attribute instance-attribute

lev_tr_source__4: Optional[int] = Field(-9, description="AGE: set -9999. in msource's AGE section")

lev_tr_source__5 class-attribute instance-attribute

lev_tr_source__5: Optional[int] = Field(-9, description='SED3D')

lev_tr_source__6 class-attribute instance-attribute

lev_tr_source__6: Optional[int] = Field(-9, description='EcoSim')

lev_tr_source__7 class-attribute instance-attribute

lev_tr_source__7: Optional[int] = Field(-9, description='ICM')

lev_tr_source__8 class-attribute instance-attribute

lev_tr_source__8: Optional[int] = Field(-9, description='CoSINE')

lev_tr_source__9 class-attribute instance-attribute

lev_tr_source__9: Optional[int] = Field(-9, description='Feco')

lev_tr_source__10 class-attribute instance-attribute

lev_tr_source__10: Optional[int] = Field(-9, description='TIMOR')

lev_tr_source__11 class-attribute instance-attribute

lev_tr_source__11: Optional[int] = Field(-9, description='FABM')

lev_tr_source__12 class-attribute instance-attribute

lev_tr_source__12: Optional[int] = Field(-9, description='DVD')

level_age class-attribute instance-attribute

level_age: Optional[list] = Field([9, -999], description='default: -999 (all levels)')

ihdif class-attribute instance-attribute

ihdif: Optional[int] = Field(0, description='')

nchi class-attribute instance-attribute

nchi: Optional[int] = Field(0, description='')

dzb_min class-attribute instance-attribute

dzb_min: Optional[float] = Field(0.5, description='needed if nchi=1; min. bottom boundary layer thickness [m].')

hmin_man class-attribute instance-attribute

hmin_man: Optional[float] = Field(1.0, description="needed if nchi=-1: min. depth in Manning's formulation [m]")

ncor class-attribute instance-attribute

ncor: Optional[int] = Field(1, description='should usually be 1 if ics=2')

rlatitude class-attribute instance-attribute

rlatitude: Optional[int] = Field(46, description='if ncor=-1')

coricoef class-attribute instance-attribute

coricoef: Optional[int] = Field(0, description='if ncor=0')

ic_elev class-attribute instance-attribute

ic_elev: Optional[int] = Field(0, description='')

nramp_elev class-attribute instance-attribute

nramp_elev: Optional[int] = Field(0, description='')

inv_atm_bnd class-attribute instance-attribute

inv_atm_bnd: Optional[int] = Field(0, description='0: off; 1: on')

prmsl_ref class-attribute instance-attribute

prmsl_ref: Optional[float] = Field(101325.0, description='reference atmos. pressure on bnd [Pa]')

flag_ic__1 class-attribute instance-attribute

flag_ic__1: Optional[int] = Field(0, description='T')

flag_ic__2 class-attribute instance-attribute

flag_ic__2: Optional[int] = Field(0, description='S')

flag_ic__3 class-attribute instance-attribute

flag_ic__3: Optional[int] = Field(1, description='GEN (user defined module)')

flag_ic__5 class-attribute instance-attribute

flag_ic__5: Optional[int] = Field(1, description='SED3D')

flag_ic__6 class-attribute instance-attribute

flag_ic__6: Optional[int] = Field(1, description='EcoSim')

flag_ic__7 class-attribute instance-attribute

flag_ic__7: Optional[int] = Field(1, description='ICM')

flag_ic__8 class-attribute instance-attribute

flag_ic__8: Optional[int] = Field(1, description='CoSINE')

flag_ic__9 class-attribute instance-attribute

flag_ic__9: Optional[int] = Field(1, description='FIB')

flag_ic__10 class-attribute instance-attribute

flag_ic__10: Optional[int] = Field(1, description='TIMOR')

flag_ic__11 class-attribute instance-attribute

flag_ic__11: Optional[int] = Field(1, description='FABM')

flag_ic__12 class-attribute instance-attribute

flag_ic__12: Optional[int] = Field(0, description='DVD (must=0)')

gen_wsett class-attribute instance-attribute

gen_wsett: Optional[float] = Field(0, description='Settling vel [m/s] for GEN module')

ibcc_mean class-attribute instance-attribute

ibcc_mean: Optional[int] = Field(0, description='')

rmaxvel class-attribute instance-attribute

rmaxvel: Optional[float] = Field(5.0, description='')

velmin_btrack class-attribute instance-attribute

velmin_btrack: Optional[float] = Field(0.0001, description='min. vel for invoking btrack and for abnormal exit in quicksearch')

btrack_nudge class-attribute instance-attribute

btrack_nudge: Optional[float] = Field(0.009013, description='Nudging factors for starting side/node')

ihhat class-attribute instance-attribute

ihhat: Optional[int] = Field(1, description='')

inunfl class-attribute instance-attribute

inunfl: Optional[int] = Field(0, description='')

h0 class-attribute instance-attribute

h0: Optional[float] = Field(0.01, description='min. water depth for wetting/drying [m]')

shorewafo class-attribute instance-attribute

shorewafo: Optional[int] = Field(0, description='Matters only if USE_WWM')

moitn0 class-attribute instance-attribute

moitn0: Optional[int] = Field(50, description='output spool for solver info; used only with JCG')

mxitn0 class-attribute instance-attribute

mxitn0: Optional[int] = Field(1500, description='max. iteration allowed')

rtol0 class-attribute instance-attribute

rtol0: Optional[float] = Field(1e-12, description='error tolerance')

nadv class-attribute instance-attribute

nadv: Optional[int] = Field(1, description='')

dtb_max class-attribute instance-attribute

dtb_max: Optional[float] = Field(30.0, description='in sec')

dtb_min class-attribute instance-attribute

dtb_min: Optional[float] = Field(10.0, description='')

inter_mom class-attribute instance-attribute

inter_mom: Optional[int] = Field(0, description='')

kr_co class-attribute instance-attribute

kr_co: Optional[int] = Field(1, description='not used if inter_mom=0')

itr_met class-attribute instance-attribute

itr_met: Optional[int] = Field(3, description='')

h_tvd class-attribute instance-attribute

h_tvd: Optional[float] = Field(5.0, description='cut-off depth (m)')

eps1_tvd_imp class-attribute instance-attribute

eps1_tvd_imp: Optional[float] = Field(0.0001, description='suggested value is 1.e-4, but for large suspended load, need to use a smaller value (e.g. 1.e-9)')

eps2_tvd_imp class-attribute instance-attribute

eps2_tvd_imp: Optional[float] = Field(1e-14, description='')

ielm_transport class-attribute instance-attribute

ielm_transport: Optional[int] = Field(0, description='1: turn on')

max_subcyc class-attribute instance-attribute

max_subcyc: Optional[int] = Field(10, description='used only if ielm_transport/=0. Max # of subcycling per time step in transport allowed')

ip_weno class-attribute instance-attribute

ip_weno: Optional[int] = Field(2, description='order of accuracy: 0- upwind; 1- linear polynomial, 2nd order; 2- quadratic polynomial, 3rd order')

courant_weno class-attribute instance-attribute

courant_weno: Optional[float] = Field(0.5, description='Courant number for weno transport')

nquad class-attribute instance-attribute

nquad: Optional[int] = Field(2, description='number of quad points on each side, nquad= 1 or 2')

ntd_weno class-attribute instance-attribute

ntd_weno: Optional[int] = Field(1, description='order of temporal discretization: (1) Euler (default); (3): 3rd-order Runge-Kutta (only for benchmarking)')

epsilon1 class-attribute instance-attribute

epsilon1: Optional[float] = Field(0.001, description='coefficient for 2nd order weno smoother (larger values are more prone to numerical dispersion)')

epsilon2 class-attribute instance-attribute

epsilon2: Optional[float] = Field(1e-10, description='1st coefficient for 3rd order weno smoother (larger values are more prone to numerical dispersion')

i_prtnftl_weno class-attribute instance-attribute

i_prtnftl_weno: Optional[int] = Field(0, description='option for writing nonfatal errors on invalid temp. or salinity for density: (0) off; (1) on.')

epsilon3 class-attribute instance-attribute

epsilon3: Optional[float] = Field(1e-25, description='2nd coefficient for 3rd order weno smoother (inactive at the moment)')

ielad_weno class-attribute instance-attribute

ielad_weno: Optional[int] = Field(0, description='ielad, if ielad=1, use ELAD method to suppress dispersion (inactive at the moment)')

small_elad class-attribute instance-attribute

small_elad: Optional[float] = Field(0.0001, description='small (inactive at the moment)')

nws class-attribute instance-attribute

nws: Optional[int] = Field(0, description='\n        Atmos. option. Use nws=2 and USE_ATMOS for coupling with atmospheric model.\n        If nws=0, no atmos. forcing is applied. If nws=1, atmos.\n        variables are read in from wind.th. If nws=2, atmos. variables are\n        read in from sflux_ files.\n        If nws=4, ascii format is used for wind and atmos. pressure at each node (see source code).\n        If nws=-1 (requires USE_PAHM), use Holland parametric wind model (barotropic only with wind and atmos. pressure).\n        In this case, the Holland model is called every step so wtiminc is not used. An extra \n        input file is needed: hurricane-track.dat, in addition to a few parameters below.\n\n        Stress calculation:\n        If nws=2, ihconsv=1 and iwind_form=0, the stress is calculated from heat exchange\n        routine; in this case USE_ATMOS cannot be on.\n        Otherwise if iwind_form=-1, the stress is calculated from Pond & Pichard formulation;\n        if iwind_form=1, Hwang (2018) formulation (Cd tapers off at high wind).\n        If WWM is enabled and icou_elfe_wwm>0 and iwind_form=-2 or -3, stress is overwritten by WWM:\n        If iwind_form=-2, stress=rho_air*ufric^2; scaled by rho_water\n        If iwind_form=-3, the stress is calculated according to the param. of Donelan et al. (1993) based on the wave age.\n        In all cases, if USE_ICE the stress in ice-covered portion is calculated by ICE routine.\n        ')

wtiminc class-attribute instance-attribute

wtiminc: Optional[float] = Field(150.0, description='time step for atmos. forcing. Default: same as dt')

drampwind class-attribute instance-attribute

drampwind: Optional[float] = Field(1.0, description='ramp-up period in days for wind (no ramp-up if <=0)')

iwindoff class-attribute instance-attribute

iwindoff: Optional[int] = Field(0, description="needed only if nws/=0; '1': needs windfactor.gr3")

iwind_form class-attribute instance-attribute

iwind_form: Optional[int] = Field(1, description='needed if nws/=0')

model_type_pahm class-attribute instance-attribute

model_type_pahm: Optional[int] = Field(10, description='only used if nws=-1: hurricane model type (1: Holland; 10: GAHM)')

ihconsv class-attribute instance-attribute

ihconsv: Optional[int] = Field(0, description='heat exchange option')

isconsv class-attribute instance-attribute

isconsv: Optional[int] = Field(0, description='evaporation/precipitation model')

i_hmin_airsea_ex class-attribute instance-attribute

i_hmin_airsea_ex: Optional[int] = Field(2, description='no effect if ihconsv=0')

hmin_airsea_ex class-attribute instance-attribute

hmin_airsea_ex: Optional[float] = Field(0.2, description='[m], no effect if ihconsv=0')

i_hmin_salt_ex class-attribute instance-attribute

i_hmin_salt_ex: Optional[int] = Field(2, description='no effect if isconsv=0')

hmin_salt_ex class-attribute instance-attribute

hmin_salt_ex: Optional[float] = Field(0.2, description='[m], no effect if isconsv=0')

iprecip_off_bnd class-attribute instance-attribute

iprecip_off_bnd: Optional[int] = Field(0, description='if /=0, precip will be turned off near land bnd')

itur class-attribute instance-attribute

itur: Optional[int] = Field(3, description='Default: 0')

dfv0 class-attribute instance-attribute

dfv0: Optional[float] = Field(0.01, description='needed if itur=0')

dfh0 class-attribute instance-attribute

dfh0: Optional[float] = Field(0.0001, description='needed if itur=0')

mid class-attribute instance-attribute

mid: Optional[str] = Field('KL', description='needed if itur=3,5. Use KE if itur=5')

stab class-attribute instance-attribute

stab: Optional[str] = Field('KC', description="needed if itur=3 or 5. Use 'GA' if turb_met='MY'; otherwise use 'KC'.")

xlsc0 class-attribute instance-attribute

xlsc0: Optional[float] = Field(0.1, description='needed if itur=3 or 5. Scale for surface & bottom mixing length (>0)')

inu_elev class-attribute instance-attribute

inu_elev: Optional[int] = Field(0, description='')

inu_uv class-attribute instance-attribute

inu_uv: Optional[int] = Field(0, description='')

inu_tr__1 class-attribute instance-attribute

inu_tr__1: Optional[int] = Field(0, description='T')

inu_tr__2 class-attribute instance-attribute

inu_tr__2: Optional[int] = Field(0, description='S')

inu_tr__3 class-attribute instance-attribute

inu_tr__3: Optional[int] = Field(0, description='GEN')

inu_tr__4 class-attribute instance-attribute

inu_tr__4: Optional[int] = Field(0, description='Age')

inu_tr__5 class-attribute instance-attribute

inu_tr__5: Optional[int] = Field(0, description='SED3D')

inu_tr__6 class-attribute instance-attribute

inu_tr__6: Optional[int] = Field(0, description='EcoSim')

inu_tr__7 class-attribute instance-attribute

inu_tr__7: Optional[int] = Field(0, description='ICM')

inu_tr__8 class-attribute instance-attribute

inu_tr__8: Optional[int] = Field(0, description='CoSINE')

inu_tr__9 class-attribute instance-attribute

inu_tr__9: Optional[int] = Field(0, description='FIB')

inu_tr__10 class-attribute instance-attribute

inu_tr__10: Optional[int] = Field(0, description='TIMOR')

inu_tr__11 class-attribute instance-attribute

inu_tr__11: Optional[int] = Field(0, description='FABM')

inu_tr__12 class-attribute instance-attribute

inu_tr__12: Optional[int] = Field(0, description='DVD (must=0)')

nu_sum_mult class-attribute instance-attribute

nu_sum_mult: Optional[int] = Field(1, description='1: final relax is sum of horizontal')

Functions

validate_ipre2 classmethod

validate_ipre2(v)
Source code in rompy_schism/namelists/param.py
@field_validator("ipre2")
@classmethod
def validate_ipre2(cls, v):
    if v not in [0, 1]:
        raise ValueError("ipre2 must be 0 or 1")
    return v

validate_itransport_only classmethod

validate_itransport_only(v)
Source code in rompy_schism/namelists/param.py
@field_validator("itransport_only")
@classmethod
def validate_itransport_only(cls, v):
    if v not in [0, 1, 2]:
        raise ValueError("itransport_only must be 0, 1, or 2")
    return v

validate_iloadtide classmethod

validate_iloadtide(v)
Source code in rompy_schism/namelists/param.py
@field_validator("iloadtide")
@classmethod
def validate_iloadtide(cls, v):
    if v not in [0, 1, 2, 3]:
        raise ValueError("iloadtide must be 0, 1, 2, or 3")
    return v

validate_loadtide_coef classmethod

validate_loadtide_coef(v)
Source code in rompy_schism/namelists/param.py
@field_validator("loadtide_coef")
@classmethod
def validate_loadtide_coef(cls, v):
    if v < 0 or v > 1:
        raise ValueError("loadtide_coef must be between 0 and 1")
    return v

validate_start_year classmethod

validate_start_year(v)
Source code in rompy_schism/namelists/param.py
@field_validator("start_year")
@classmethod
def validate_start_year(cls, v):
    if v < 1900 or v > 2100:
        raise ValueError("start_year must be between 1900 and 2100")
    return v

validate_start_month classmethod

validate_start_month(v)
Source code in rompy_schism/namelists/param.py
@field_validator("start_month")
@classmethod
def validate_start_month(cls, v):
    if v < 1 or v > 12:
        raise ValueError("start_month must be between 1 and 12")
    return v

check_loadtide_coef

check_loadtide_coef()
Source code in rompy_schism/namelists/param.py
@model_validator(mode="after")
def check_loadtide_coef(self):
    if self.iloadtide in [2, 3] and self.loadtide_coef == 0:
        raise ValueError("loadtide_coef must be set when iloadtide is 2 or 3")
    return self

Schout

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/param.py
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
class Schout(NamelistBaseModel):
    nc_out: Optional[int] = Field(
        1,
        description="Main switch to control netcdf output. If 0, SCHISM won't output nc files at all.",
    )
    iof_ugrid: Optional[int] = Field(
        0,
        description="UGRID option for 3D outputs. If non-zero, 3D outputs will have UGRID metadata.",
    )
    nhot: Optional[int] = Field(
        0,
        description="Option for hotstart outputs. If 1, outputs hotstart every 'nhot_write' steps.",
    )
    nhot_write: Optional[int] = Field(
        8640,
        description="Number of steps between hotstart outputs. Must be a multiple of ihfskip if nhot=1.",
    )
    iout_sta: Optional[int] = Field(
        0,
        description="Station output option. If non-zero, requires output skip (nspool_sta) and a station.in file.",
    )
    nspool_sta: Optional[int] = Field(
        10,
        description="Number of steps between station outputs. Required if iout_sta is non-zero.",
    )
    iof_hydro__1: Optional[int] = Field(
        1, description="0: off; 1: on - elev. [m]  {elevation}  2D"
    )
    iof_hydro__2: Optional[int] = Field(
        0, description="air pressure [Pa]  {airPressure}  2D"
    )
    iof_hydro__3: Optional[int] = Field(
        0, description="air temperature [C] {airTemperature}  2D"
    )
    iof_hydro__4: Optional[int] = Field(
        0, description="Specific humidity [-] {specificHumidity}  2D"
    )
    iof_hydro__5: Optional[int] = Field(
        0,
        description="Net downward solar (shortwave) radiation after albedo [W/m/m] {solarRadiation}  2D",
    )
    iof_hydro__6: Optional[int] = Field(
        0, description="sensible flux (positive upward) [W/m/m]  {sensibleHeat}  2D"
    )
    iof_hydro__7: Optional[int] = Field(
        0, description="latent heat flux (positive upward) [W/m/m] {latentHeat}  2D"
    )
    iof_hydro__8: Optional[int] = Field(
        0,
        description="upward longwave radiation (positive upward) [W/m/m] {upwardLongwave}  2D",
    )
    iof_hydro__9: Optional[int] = Field(
        0,
        description="downward longwave radiation (positive downward) [W/m/m] {downwardLongwave}  2D",
    )
    iof_hydro__10: Optional[int] = Field(
        0, description="total flux=-flsu-fllu-(radu-radd) [W/m/m] {totalHeat}  2D"
    )
    iof_hydro__11: Optional[int] = Field(
        0, description="evaporation rate [kg/m/m/s] {evaporationRate}  2D"
    )
    iof_hydro__12: Optional[int] = Field(
        0, description="precipitation rate [kg/m/m/s] {precipitationRate}  2D"
    )
    iof_hydro__13: Optional[int] = Field(
        0,
        description="Bottom stress vector [kg/m/s^2(Pa)] {bottomStressX,Y}  2D vector",
    )
    iof_hydro__14: Optional[int] = Field(
        0, description="wind velocity vector [m/s] {windSpeedX,Y}  2D vector"
    )
    iof_hydro__15: Optional[int] = Field(
        0, description="wind stress vector [m^2/s/s] {windStressX,Y}  2D vector"
    )
    iof_hydro__16: Optional[int] = Field(
        1, description="depth-averaged vel vector [m/s] {depthAverageVelX,Y}  2D vector"
    )
    iof_hydro__17: Optional[int] = Field(
        0, description="vertical velocity [m/s] {verticalVelocity}  3D"
    )
    iof_hydro__18: Optional[int] = Field(
        0, description="water temperature [C] {temperature}  3D"
    )
    iof_hydro__19: Optional[int] = Field(
        0, description="water salinity [PSU] {salinity}  3D"
    )
    iof_hydro__20: Optional[int] = Field(
        0, description="water density [kg/m^3] {waterDensity}  3D"
    )
    iof_hydro__21: Optional[int] = Field(
        0, description="vertical eddy diffusivity [m^2/s] {diffusivity}  3D"
    )
    iof_hydro__22: Optional[int] = Field(
        0, description="vertical eddy viscosity [m^2/s] {viscosity}  3D"
    )
    iof_hydro__23: Optional[int] = Field(
        0, description="turbulent kinetic energy {turbulentKineticEner}   3D"
    )
    iof_hydro__24: Optional[int] = Field(
        0, description="turbulent mixing length [m] {mixingLength}  3D"
    )
    iof_hydro__26: Optional[int] = Field(
        1, description="horizontal vel vector [m/s] {horizontalVelX,Y} 3D vector"
    )
    iof_hydro__27: Optional[int] = Field(
        0,
        description="horizontal vel vector defined @side [m/s] {horizontalSideVelX,Y} 3D vector",
    )
    iof_hydro__28: Optional[int] = Field(
        0, description="vertical vel. @elem [m/s] {verticalVelAtElement} 3D"
    )
    iof_hydro__29: Optional[int] = Field(
        0, description="T @prism centers [C] {temperatureAtElement} 3D"
    )
    iof_hydro__30: Optional[int] = Field(
        0, description="S @prism centers [PSU] {salinityAtElement} 3D"
    )
    iof_hydro__31: Optional[int] = Field(
        0,
        description="Barotropic pressure gradient force vector (m.s-2) @side centers  {pressure_gradient} 2D vector",
    )
    iof_wwm__1: Optional[int] = Field(
        0, description="sig. height (m) {sigWaveHeight}   2D"
    )
    iof_wwm__2: Optional[int] = Field(
        0, description="Mean average period (sec) - TM01 {meanWavePeriod}  2D"
    )
    iof_wwm__3: Optional[int] = Field(
        0,
        description="Zero down crossing period for comparison with buoy (s) - TM02 {zeroDowncrossPeriod}  2D",
    )
    iof_wwm__4: Optional[int] = Field(
        0, description="Average period of wave runup/overtopping - TM10 {TM10}  2D"
    )
    iof_wwm__5: Optional[int] = Field(
        0, description="Mean wave number (1/m) {meanWaveNumber}  2D"
    )
    iof_wwm__6: Optional[int] = Field(
        0, description="Mean wave length (m) {meanWaveLength}  2D"
    )
    iof_wwm__7: Optional[int] = Field(
        0,
        description="Mean average energy transport direction (degr) - MWD in NDBC? {meanWaveDirection}  2D",
    )
    iof_wwm__8: Optional[int] = Field(
        0, description="Mean directional spreading (degr) {meanDirSpreading}  2D"
    )
    iof_wwm__9: Optional[int] = Field(
        0, description="Discrete peak period (sec) - Tp {peakPeriod}  2D"
    )
    iof_wwm__10: Optional[int] = Field(
        0,
        description="Continuous peak period based on higher order moments (sec) {continuousPeakPeriod}  2D",
    )
    iof_wwm__11: Optional[int] = Field(
        0, description="Peak phase vel. (m/s) {peakPhaseVel}  2D"
    )
    iof_wwm__12: Optional[int] = Field(
        0, description="Peak n-factor {peakNFactor}   2D"
    )
    iof_wwm__13: Optional[int] = Field(
        0, description="Peak group vel. (m/s) {peakGroupVel}   2D"
    )
    iof_wwm__14: Optional[int] = Field(
        0, description="Peak wave number {peakWaveNumber}  2D"
    )
    iof_wwm__15: Optional[int] = Field(
        0, description="Peak wave length {peakWaveLength}  2D"
    )
    iof_wwm__16: Optional[int] = Field(
        0, description="Peak (dominant) direction (degr) {dominantDirection}  2D"
    )
    iof_wwm__17: Optional[int] = Field(
        0, description="Peak directional spreading {peakSpreading}  2D"
    )
    iof_wwm__18: Optional[int] = Field(
        0, description="Discrete peak direction (radian?) {discretePeakDirectio}  2D"
    )
    iof_wwm__19: Optional[int] = Field(
        0, description="Orbital vel. (m/s) {orbitalVelocity}  2D"
    )
    iof_wwm__20: Optional[int] = Field(
        0, description="RMS Orbital vel. (m/s) {rmsOrbitalVelocity}  2D"
    )
    iof_wwm__21: Optional[int] = Field(
        0, description="Bottom excursion period (sec?) {bottomExcursionPerio}  2D"
    )
    iof_wwm__22: Optional[int] = Field(
        0, description="Bottom wave period (sec) {bottomWavePeriod}  2D"
    )
    iof_wwm__23: Optional[int] = Field(
        0, description="Uresell number based on peak period {UresellNumber}  2D"
    )
    iof_wwm__24: Optional[int] = Field(
        0, description="Friction velocity (m/s?) {frictionalVelocity}  2D"
    )
    iof_wwm__25: Optional[int] = Field(
        0, description="Charnock coefficient {CharnockCoeff}  2D"
    )
    iof_wwm__26: Optional[int] = Field(
        0, description="Rougness length {rougnessLength}  2D"
    )
    iof_wwm__27: Optional[int] = Field(
        0, description="Roller energy dissipation rate (W/m²) @nodes {Drol} 2D"
    )
    iof_wwm__28: Optional[int] = Field(
        0,
        description="Total wave energy dissipation rate by depth-induced breaking (W/m²) @nodes {wave_sbrtot}  2D",
    )
    iof_wwm__29: Optional[int] = Field(
        0,
        description="Total wave energy dissipation rate by bottom friction (W/m²) @nodes {wave_sbftot} 2D",
    )
    iof_wwm__30: Optional[int] = Field(
        0,
        description="Total wave energy dissipation rate by whitecapping (W/m²) @nodes {wave_sdstot} 2D",
    )
    iof_wwm__31: Optional[int] = Field(
        0,
        description="Total wave energy dissipation rate by vegetation (W/m²) @nodes {wave_svegtot} 2D",
    )
    iof_wwm__32: Optional[int] = Field(
        0,
        description="Total wave energy input rate from atmospheric forcing (W/m²) @nodes {wave_sintot} 2D",
    )
    iof_wwm__33: Optional[int] = Field(
        0, description="WWM_energy vector {waveEnergyDirX,Y}  2D vector"
    )
    iof_wwm__34: Optional[int] = Field(
        0,
        description="Vertical Stokes velocity (m.s-1) @sides and whole levels {stokes_wvel}  3D",
    )
    iof_wwm__35: Optional[int] = Field(
        0,
        description="Wave force vector (m.s-2) computed by wwm @side centers and whole levels {waveForceX,Y}   3D vector",
    )
    iof_wwm__36: Optional[int] = Field(
        0,
        description="Horizontal Stokes velocity (m.s-1) @nodes and whole levels {stokes_hvel} 3D vector",
    )
    iof_wwm__37: Optional[int] = Field(
        0,
        description="Roller contribution to horizontal Stokes velocity (m.s-1) @nodes and whole levels {roller_stokes_hvel} 3D vector",
    )
    # iof_gen__1: Optional[int] = Field(0, description="1st tracer {GEN_1}  3D")
    # iof_gen__2: Optional[int] = Field(0, description="2nd tracer {GEN_2}  3D")
    # iof_age__1: Optional[int] = Field(0, description="{AGE_1}  3D")
    # iof_age__2: Optional[int] = Field(0, description="{AGE_2}  3D")
    # iof_sed__1: Optional[int] = Field(
    #     0, description="total bed thickness @elem (m) {sedBedThickness}  2D"
    # )
    # iof_sed__2: Optional[int] = Field(
    #     0, description="total bed age over all layers @elem (sec) {sedBedAge}  2D"
    # )
    # iof_sed__3: Optional[int] = Field(
    #     0,
    #     description="Sediment transport roughness length @elem (m) (sedTransportRough) {z0st}  2D",
    # )
    # iof_sed__4: Optional[int] = Field(
    #     0,
    #     description="current-ripples roughness length @elem (m) (sedRoughCurrentRippl) {z0cr}  2D",
    # )
    # iof_sed__5: Optional[int] = Field(
    #     0,
    #     description="sand-waves roughness length (m) @elem (z0sw_elem) {sedRoughSandWave}  2D",
    # )
    # iof_sed__6: Optional[int] = Field(
    #     0,
    #     description="wave-ripples roughness length @elem (m) (z0wr_elem) {sedRoughWaveRipple}  2D",
    # )
    # iof_sed__7: Optional[int] = Field(
    #     0,
    #     description="bottom depth _change_ from init. condition (m) {sedDepthChange}  2D",
    # )
    # iof_sed__8: Optional[int] = Field(
    #     0, description="Bed median grain size in the active layer (mm) {sedD50}  2D"
    # )
    # iof_sed__9: Optional[int] = Field(
    #     0, description="Bottom shear stress (Pa) {sedBedStress}  2D"
    # )
    # iof_sed__10: Optional[int] = Field(
    #     0, description="Bottom roughness lenghth (mm) {sedBedRoughness}  2D"
    # )
    # iof_sed__11: Optional[int] = Field(
    #     0, description="sediment porosity in the top layer (-) {sedPorocity}  2D"
    # )
    # iof_sed__12: Optional[int] = Field(
    #     0,
    #     description="erosion flux for suspended transport (kg/m/m/s) {sedErosionalFlux}  2D",
    # )
    # iof_sed__13: Optional[int] = Field(
    #     0,
    #     description="deposition flux for suspended transport (kg/m/m/s) {sedDepositionalFlux}  2D",
    # )
    # iof_sed__14: Optional[int] = Field(
    #     0,
    #     description="Bedload transport rate vector due to wave acceleration (kg/m/s) {sedBedloadTransportX,Y}  2D vector",
    # )
    # iof_sed__15: Optional[int] = Field(
    #     0,
    #     description="Bedload transport rate _vector_ (kg.m-1.s-1) for 1st tracer (one output per class) {sedBedload[X,Y]_1}  2D vector",
    # )
    # iof_sed__16: Optional[int] = Field(
    #     0,
    #     description="Bedload transport of 2nd class {sedBedFraction_[X,Y]_2}  2D vector",
    # )
    # iof_sed__17: Optional[int] = Field(
    #     0,
    #     description="Bed fraction 1st tracer (one output per class) [-] {sedBedFraction_1}   2D",
    # )
    # iof_sed__18: Optional[int] = Field(
    #     0, description="Bed fraction of 2nd class {sedBedFraction_2}   2D"
    # )
    # iof_sed__19: Optional[int] = Field(
    #     0,
    #     description="conc. of 1st class (one output need by each class) [g/L] {sedConcentration_1}   3D",
    # )
    # iof_sed__20: Optional[int] = Field(
    #     0, description="conc. of 2nd class {sedConcentration_2}   3D"
    # )
    # iof_sed__21: Optional[int] = Field(
    #     0, description="total suspended concentration (g/L) {totalSuspendedLoad}  3D"
    # )
    # # iof_eco__1: Optional[list] = Field([0, "{ECO_1}", "3D"], description="") # TODO: Ask Vanessa
    # iof_eco__1: Optional[int] = Field(0, description="")
    # iof_icm_core__1: Optional[int] = Field(1, description="PB1")
    # iof_icm_core__2: Optional[int] = Field(1, description="PB2")
    # iof_icm_core__3: Optional[int] = Field(1, description="PB3")
    # iof_icm_core__4: Optional[int] = Field(1, description="RPOC")
    # iof_icm_core__5: Optional[int] = Field(1, description="LPOC")
    # iof_icm_core__6: Optional[int] = Field(1, description="DOC")
    # iof_icm_core__7: Optional[int] = Field(1, description="RPON")
    # iof_icm_core__8: Optional[int] = Field(1, description="LPON")
    # iof_icm_core__9: Optional[int] = Field(1, description="DON")
    # iof_icm_core__10: Optional[int] = Field(1, description="NH4")
    # iof_icm_core__11: Optional[int] = Field(1, description="NO3")
    # iof_icm_core__12: Optional[int] = Field(1, description="RPOP")
    # iof_icm_core__13: Optional[int] = Field(1, description="LPOP")
    # iof_icm_core__14: Optional[int] = Field(1, description="DOP")
    # iof_icm_core__15: Optional[int] = Field(1, description="PO4")
    # iof_icm_core__16: Optional[int] = Field(1, description="COD")
    # iof_icm_core__17: Optional[int] = Field(1, description="DOX")
    # iof_icm_silica__1: Optional[int] = Field(1, description="SU")
    # iof_icm_silica__2: Optional[int] = Field(1, description="SA")
    # iof_icm_zb__1: Optional[int] = Field(1, description="ZB1")
    # iof_icm_zb__2: Optional[int] = Field(1, description="ZB2")
    # iof_icm_ph__1: Optional[int] = Field(1, description="TIC")
    # iof_icm_ph__2: Optional[int] = Field(1, description="ALK")
    # iof_icm_ph__3: Optional[int] = Field(1, description="CA")
    # iof_icm_ph__4: Optional[int] = Field(1, description="CACO3")
    # iof_icm_cbp__1: Optional[int] = Field(1, description="SRPOC")
    # iof_icm_cbp__2: Optional[int] = Field(1, description="SRPON")
    # iof_icm_cbp__3: Optional[int] = Field(1, description="SRPOP")
    # iof_icm_cbp__4: Optional[int] = Field(1, description="PIP")
    # iof_icm_sav__1: Optional[int] = Field(
    #     1, description="stleaf: total leaf biomass @elem [gC/m^2]"
    # )
    # iof_icm_sav__2: Optional[int] = Field(
    #     1, description="ststem: total stem biomass @elem [gC/m^2]"
    # )
    # iof_icm_sav__3: Optional[int] = Field(
    #     1, description="stroot: total root biomass @elem [gC/m^2]"
    # )
    # iof_icm_sav__4: Optional[int] = Field(
    #     1, description="sht:    canopy height @elem [m]"
    # )
    # iof_icm_veg__1: Optional[int] = Field(
    #     1, description="vtleaf1: leaf biomass group 1 [gC/m^2]"
    # )
    # iof_icm_veg__2: Optional[int] = Field(
    #     1, description="vtleaf2: leaf biomass group 2 [gC/m^2]"
    # )
    # iof_icm_veg__3: Optional[int] = Field(
    #     1, description="vtleaf3: leaf biomass group 3 [gC/m^2]"
    # )
    # iof_icm_veg__4: Optional[int] = Field(
    #     1, description="vtstem1: stem biomass group 1 [gC/m^2]"
    # )
    # iof_icm_veg__5: Optional[int] = Field(
    #     1, description="vtstem2: stem biomass group 2 [gC/m^2]"
    # )
    # iof_icm_veg__6: Optional[int] = Field(
    #     1, description="vtstem3: stem biomass group 3 [gC/m^2]"
    # )
    # iof_icm_veg__7: Optional[int] = Field(
    #     1, description="vtroot1: root biomass group 1 [gC/m^2]"
    # )
    # iof_icm_veg__8: Optional[int] = Field(
    #     1, description="vtroot2: root biomass group 2 [gC/m^2]"
    # )
    # iof_icm_veg__9: Optional[int] = Field(
    #     1, description="vtroot3: root biomass group 3 [gC/m^2]"
    # )
    # iof_icm_veg__10: Optional[int] = Field(
    #     1, description="vht1:    canopy height group 1 [m]"
    # )
    # iof_icm_veg__11: Optional[int] = Field(
    #     1, description="vht2:    canopy height group 2 [m]"
    # )
    # iof_icm_veg__12: Optional[int] = Field(
    #     1, description="vht3:    canopy height group 3 [m]"
    # )
    # iof_icm_sed__1: Optional[int] = Field(1, description="bPOC1 (g.m-3)")
    # iof_icm_sed__2: Optional[int] = Field(1, description="bPOC2 (g.m-3)")
    # iof_icm_sed__3: Optional[int] = Field(1, description="bPOC3 (g.m-3)")
    # iof_icm_sed__4: Optional[int] = Field(1, description="bPON1 (g.m-3)")
    # iof_icm_sed__5: Optional[int] = Field(1, description="bPON2 (g.m-3)")
    # iof_icm_sed__6: Optional[int] = Field(1, description="bPON3 (g.m-3)")
    # iof_icm_sed__7: Optional[int] = Field(1, description="bPOP1 (g.m-3)")
    # iof_icm_sed__8: Optional[int] = Field(1, description="bPOP2 (g.m-3)")
    # iof_icm_sed__9: Optional[int] = Field(1, description="bPOP3 (g.m-3)")
    # iof_icm_sed__10: Optional[int] = Field(1, description="bNH4  (g.m-3)")
    # iof_icm_sed__11: Optional[int] = Field(1, description="bNO3  (g.m-3)")
    # iof_icm_sed__12: Optional[int] = Field(1, description="bPO4  (g.m-3)")
    # iof_icm_sed__13: Optional[int] = Field(1, description="bH2S  (g.m-3)")
    # iof_icm_sed__14: Optional[int] = Field(1, description="bCH4  (g.m-3)")
    # iof_icm_sed__15: Optional[int] = Field(1, description="bPOS  (g.m-3)")
    # iof_icm_sed__16: Optional[int] = Field(1, description="bSA   (g.m-3)")
    # iof_icm_sed__17: Optional[int] = Field(
    #     1, description="bstc: surface transfer coeff. (m/day)"
    # )
    # iof_icm_sed__18: Optional[int] = Field(
    #     1, description="bSTR: benthic stress      (day)"
    # )
    # iof_icm_sed__19: Optional[int] = Field(
    #     1, description="bThp: consective days of hypoxia (day)"
    # )
    # iof_icm_sed__20: Optional[int] = Field(
    #     1, description="bTox: consective days of oxic condition after hypoxia (day)"
    # )
    # iof_icm_sed__21: Optional[int] = Field(1, description="SOD   (g.m-2.day-1)")
    # iof_icm_sed__22: Optional[int] = Field(1, description="JNH4  (g.m-2.day-1)")
    # iof_icm_sed__23: Optional[int] = Field(1, description="JNO3  (g.m-2.day-1)")
    # iof_icm_sed__24: Optional[int] = Field(1, description="JPO4  (g.m-2.day-1)")
    # iof_icm_sed__25: Optional[int] = Field(1, description="JSA   (g.m-2.day-1)")
    # iof_icm_sed__26: Optional[int] = Field(1, description="JCOD  (g.m-2.day-1)")
    # iof_icm_ba__1: Optional[int] = Field(1, description="BA    (g[C].m-2)")
    # iof_icm_dbg__1: Optional[int] = Field(1, description="2D ICM debug variables")
    # iof_icm_dbg__2: Optional[int] = Field(1, description="3D ICM debug variables")
    # iof_cos__1: Optional[int] = Field(0, description="NO3 (uM)")
    # iof_cos__2: Optional[int] = Field(0, description="SiO4(uM)")
    # iof_cos__3: Optional[int] = Field(0, description="NH4 (uM)")
    # iof_cos__4: Optional[int] = Field(0, description="S1  (uM)")
    # iof_cos__5: Optional[int] = Field(0, description="S2  (uM)")
    # iof_cos__6: Optional[int] = Field(0, description="Z1  (uM)")
    # iof_cos__7: Optional[int] = Field(0, description="Z2  (uM)")
    # iof_cos__8: Optional[int] = Field(0, description="DN  (uM)")
    # iof_cos__9: Optional[int] = Field(0, description="DSi (uM)")
    # iof_cos__10: Optional[int] = Field(0, description="PO4 (uM)")
    # iof_cos__11: Optional[int] = Field(0, description="DOX (uM)")
    # iof_cos__12: Optional[int] = Field(0, description="CO2 (uM)")
    # iof_cos__13: Optional[int] = Field(0, description="ALK (uM)")
    # iof_fib__1: Optional[int] = Field(0, description="{FIB_1}  3D")
    # iof_sed2d__1: Optional[int] = Field(
    #     0,
    #     description="bottom depth _change_ from init. condition (m) {SED2D_depth_change}",
    # )
    # iof_sed2d__2: Optional[int] = Field(
    #     0, description="drag coefficient used in transport formulae SED2D_Cd{}"
    # )
    # iof_sed2d__3: Optional[int] = Field(
    #     0, description="Courant number (b.qtot.dt / h.dx) {SED2D_cflsed}"
    # )
    # iof_sed2d__4: Optional[int] = Field(0, description="Top layer d50 (m) {SED2D_d50}")
    # iof_sed2d__5: Optional[int] = Field(
    #     0, description="total transport rate vector (kg/m/s) {SED2D_total_transport}"
    # )
    # iof_sed2d__6: Optional[int] = Field(
    #     0, description="suspended tranport rate vector (kg/m/s) {SED2D_susp_load}"
    # )
    # iof_sed2d__7: Optional[int] = Field(
    #     0, description="bedload transport rate vector (kg/m/s) {SED2D_bed_load}"
    # )
    # iof_sed2d__8: Optional[int] = Field(
    #     0,
    #     description="time averaged total transport rate vector (kg/m/s) {SED2D_average_transport}",
    # )
    # iof_sed2d__9: Optional[int] = Field(
    #     0, description="bottom slope vector (m/m); negative uphill {SED2D_bottom_slope}"
    # )
    # iof_sed2d__10: Optional[int] = Field(
    #     0, description="Total roughness length @elem (m) (z0eq) {z0eq}"
    # )
    # iof_sed2d__11: Optional[int] = Field(
    #     0, description="current-ripples roughness length @elem (m) (z0cr) {z0cr}"
    # )
    # iof_sed2d__12: Optional[int] = Field(
    #     0, description="sand-waves roughness length @elem (m) (z0sw) {z0sw}"
    # )
    # iof_sed2d__13: Optional[int] = Field(
    #     0, description="wave-ripples roughness length @elem (m) (z0wr) {z0wr}"
    # )
    # iof_ice__1: Optional[int] = Field(
    #     0, description="divergence @ elem ('Delta') [1/sec] {iceStrainRate}  2D"
    # )
    # iof_ice__2: Optional[int] = Field(
    #     0, description="ice advective velcoity vector [m/s] {iceVelocityX,Y}  2D vector"
    # )
    # iof_ice__3: Optional[int] = Field(
    #     0,
    #     description="net heat flux to ocean (>0 warm up SST) [W/m/m] {iceNetHeatFlux}  2D",
    # )
    # iof_ice__4: Optional[int] = Field(
    #     0,
    #     description="net fresh water flux to ocean (>0 freshens up SSS) [kg/s/m/m] {iceFreshwaterFlux}  2D",
    # )
    # iof_ice__5: Optional[int] = Field(
    #     0,
    #     description="ice temperature [C] at air-ice interface {iceTopTemperature}  2D",
    # )
    # iof_ice__6: Optional[int] = Field(
    #     0, description="ice volume [m] {iceTracer_1}   2D"
    # )
    # iof_ice__7: Optional[int] = Field(
    #     0, description="ice concentration [-] {iceTracer_2}  2D"
    # )
    # iof_ice__8: Optional[int] = Field(
    #     0, description="snow volume [m] {iceTracer_3}  2D"
    # )
    # iof_ana__1: Optional[int] = Field(
    #     0,
    #     description="min time step at each element over all subcycles in horizontal transport solver [s]   {minTransportTimeStep}  2D",
    # )
    # iof_ana__2: Optional[int] = Field(
    #     0,
    #     description="x-component of \nabla air_pres /\rho_0 [m/s/s] {airPressureGradientX}  2D",
    # )
    # iof_ana__3: Optional[int] = Field(
    #     0,
    #     description="y-component of \nabla air_pres /\rho_0 [m/s/s] {airPressureGradientY}  2D",
    # )
    # iof_ana__4: Optional[int] = Field(
    #     0,
    #     description="\alpha*g*\nabla \Psi [m/s/s] (gradient of tidal potential) {tidePotentialGradX}  2D",
    # )
    # iof_ana__5: Optional[int] = Field(
    #     0, description="\alpha*g*\nabla \Psi [m/s/s] {tidePotentialGradY}  2D"
    # )
    # iof_ana__6: Optional[int] = Field(
    #     0,
    #     description="\nabla \cdot (\mu \nabla u) [m/s/s] (horizontal momentum mixing) {horzontalViscosityX}  3D side",
    # )
    # iof_ana__7: Optional[int] = Field(
    #     0,
    #     description="\nabla \cdot (\mu \nabla v) [m/s/s] {horzontalViscosityY}   3D side",
    # )
    # iof_ana__8: Optional[int] = Field(
    #     0,
    #     description="-g/rho0* \int_z^\eta dr_dx dz  [m/s/s] (b-clinic gradient) {baroclinicForceX}  3D side",
    # )
    # iof_ana__9: Optional[int] = Field(
    #     0,
    #     description="-g/rho0* \int_z^\eta dr_dy dz  [m/s/s] {baroclinicForceY}  3D side",
    # )
    # iof_ana__10: Optional[int] = Field(
    #     0,
    #     description="d (\nu du/dz)/dz [m/s/s] - no vegetation effects (vertical momentum mixing) {verticalViscosityX}  3D side",
    # )
    # iof_ana__11: Optional[int] = Field(
    #     0,
    #     description="d (\nu dv/dz)/dz [m/s/s] - no vegetation effects {verticalViscosityY}  3D side",
    # )
    # iof_ana__12: Optional[int] = Field(
    #     0,
    #     description="(u \cdot \nabla) u [m/s/s] (momentum advection) {mommentumAdvectionX}  3D side",
    # )
    # iof_ana__13: Optional[int] = Field(
    #     0, description="(u \cdot \nabla) u [m/s/s] {mommentumAdvectionY}  3D side"
    # )
    # iof_ana__14: Optional[int] = Field(
    #     0, description="gradient Richardson number [-] {gradientRichardson}   3D"
    # )

    @field_validator("nc_out")
    @classmethod
    def validate_nc_out(cls, v):
        if v not in [0, 1]:
            raise ValueError("nc_out must be 0 or 1")
        return v

    @field_validator("iof_ugrid")
    @classmethod
    def validate_iof_ugrid(cls, v):
        if v < 0:
            raise ValueError("iof_ugrid must be non-negative")
        return v

    @field_validator("nhot")
    @classmethod
    def validate_nhot(cls, v):
        if v not in [0, 1]:
            raise ValueError("nhot must be 0 or 1")
        return v

    @field_validator("nhot_write")
    @classmethod
    def validate_nhot_write(cls, v):
        if v <= 0:
            raise ValueError("nhot_write must be positive")
        return v

    @field_validator("iout_sta")
    @classmethod
    def validate_iout_sta(cls, v):
        if v < 0:
            raise ValueError("iout_sta must be non-negative")
        return v

    @field_validator("nspool_sta")
    @classmethod
    def validate_nspool_sta(cls, v):
        if v <= 0:
            raise ValueError("nspool_sta must be positive")
        return v

    @model_validator(mode="after")
    def validate_nhot_write_multiple(self):
        if self.nhot == 1:
            if self.nhot_write % self.ihfskip != 0:
                raise ValueError("nhot_write must be a multiple of ihfskip when nhot=1")
            if self.ihfskip % self.dt != 0:
                raise ValueError("ihfskip must be a multiple of dt")
        return self

    @model_validator(mode="after")
    def validate_nspool_sta_requirement(self):
        if self.iout_sta != 0 and self.nspool_sta <= 0:
            raise ValueError("nspool_sta must be positive when iout_sta is non-zero")
        return self

Attributes

nc_out class-attribute instance-attribute

nc_out: Optional[int] = Field(1, description="Main switch to control netcdf output. If 0, SCHISM won't output nc files at all.")

iof_ugrid class-attribute instance-attribute

iof_ugrid: Optional[int] = Field(0, description='UGRID option for 3D outputs. If non-zero, 3D outputs will have UGRID metadata.')

nhot class-attribute instance-attribute

nhot: Optional[int] = Field(0, description="Option for hotstart outputs. If 1, outputs hotstart every 'nhot_write' steps.")

nhot_write class-attribute instance-attribute

nhot_write: Optional[int] = Field(8640, description='Number of steps between hotstart outputs. Must be a multiple of ihfskip if nhot=1.')

iout_sta class-attribute instance-attribute

iout_sta: Optional[int] = Field(0, description='Station output option. If non-zero, requires output skip (nspool_sta) and a station.in file.')

nspool_sta class-attribute instance-attribute

nspool_sta: Optional[int] = Field(10, description='Number of steps between station outputs. Required if iout_sta is non-zero.')

iof_hydro__1 class-attribute instance-attribute

iof_hydro__1: Optional[int] = Field(1, description='0: off; 1: on - elev. [m]  {elevation}  2D')

iof_hydro__2 class-attribute instance-attribute

iof_hydro__2: Optional[int] = Field(0, description='air pressure [Pa]  {airPressure}  2D')

iof_hydro__3 class-attribute instance-attribute

iof_hydro__3: Optional[int] = Field(0, description='air temperature [C] {airTemperature}  2D')

iof_hydro__4 class-attribute instance-attribute

iof_hydro__4: Optional[int] = Field(0, description='Specific humidity [-] {specificHumidity}  2D')

iof_hydro__5 class-attribute instance-attribute

iof_hydro__5: Optional[int] = Field(0, description='Net downward solar (shortwave) radiation after albedo [W/m/m] {solarRadiation}  2D')

iof_hydro__6 class-attribute instance-attribute

iof_hydro__6: Optional[int] = Field(0, description='sensible flux (positive upward) [W/m/m]  {sensibleHeat}  2D')

iof_hydro__7 class-attribute instance-attribute

iof_hydro__7: Optional[int] = Field(0, description='latent heat flux (positive upward) [W/m/m] {latentHeat}  2D')

iof_hydro__8 class-attribute instance-attribute

iof_hydro__8: Optional[int] = Field(0, description='upward longwave radiation (positive upward) [W/m/m] {upwardLongwave}  2D')

iof_hydro__9 class-attribute instance-attribute

iof_hydro__9: Optional[int] = Field(0, description='downward longwave radiation (positive downward) [W/m/m] {downwardLongwave}  2D')

iof_hydro__10 class-attribute instance-attribute

iof_hydro__10: Optional[int] = Field(0, description='total flux=-flsu-fllu-(radu-radd) [W/m/m] {totalHeat}  2D')

iof_hydro__11 class-attribute instance-attribute

iof_hydro__11: Optional[int] = Field(0, description='evaporation rate [kg/m/m/s] {evaporationRate}  2D')

iof_hydro__12 class-attribute instance-attribute

iof_hydro__12: Optional[int] = Field(0, description='precipitation rate [kg/m/m/s] {precipitationRate}  2D')

iof_hydro__13 class-attribute instance-attribute

iof_hydro__13: Optional[int] = Field(0, description='Bottom stress vector [kg/m/s^2(Pa)] {bottomStressX,Y}  2D vector')

iof_hydro__14 class-attribute instance-attribute

iof_hydro__14: Optional[int] = Field(0, description='wind velocity vector [m/s] {windSpeedX,Y}  2D vector')

iof_hydro__15 class-attribute instance-attribute

iof_hydro__15: Optional[int] = Field(0, description='wind stress vector [m^2/s/s] {windStressX,Y}  2D vector')

iof_hydro__16 class-attribute instance-attribute

iof_hydro__16: Optional[int] = Field(1, description='depth-averaged vel vector [m/s] {depthAverageVelX,Y}  2D vector')

iof_hydro__17 class-attribute instance-attribute

iof_hydro__17: Optional[int] = Field(0, description='vertical velocity [m/s] {verticalVelocity}  3D')

iof_hydro__18 class-attribute instance-attribute

iof_hydro__18: Optional[int] = Field(0, description='water temperature [C] {temperature}  3D')

iof_hydro__19 class-attribute instance-attribute

iof_hydro__19: Optional[int] = Field(0, description='water salinity [PSU] {salinity}  3D')

iof_hydro__20 class-attribute instance-attribute

iof_hydro__20: Optional[int] = Field(0, description='water density [kg/m^3] {waterDensity}  3D')

iof_hydro__21 class-attribute instance-attribute

iof_hydro__21: Optional[int] = Field(0, description='vertical eddy diffusivity [m^2/s] {diffusivity}  3D')

iof_hydro__22 class-attribute instance-attribute

iof_hydro__22: Optional[int] = Field(0, description='vertical eddy viscosity [m^2/s] {viscosity}  3D')

iof_hydro__23 class-attribute instance-attribute

iof_hydro__23: Optional[int] = Field(0, description='turbulent kinetic energy {turbulentKineticEner}   3D')

iof_hydro__24 class-attribute instance-attribute

iof_hydro__24: Optional[int] = Field(0, description='turbulent mixing length [m] {mixingLength}  3D')

iof_hydro__26 class-attribute instance-attribute

iof_hydro__26: Optional[int] = Field(1, description='horizontal vel vector [m/s] {horizontalVelX,Y} 3D vector')

iof_hydro__27 class-attribute instance-attribute

iof_hydro__27: Optional[int] = Field(0, description='horizontal vel vector defined @side [m/s] {horizontalSideVelX,Y} 3D vector')

iof_hydro__28 class-attribute instance-attribute

iof_hydro__28: Optional[int] = Field(0, description='vertical vel. @elem [m/s] {verticalVelAtElement} 3D')

iof_hydro__29 class-attribute instance-attribute

iof_hydro__29: Optional[int] = Field(0, description='T @prism centers [C] {temperatureAtElement} 3D')

iof_hydro__30 class-attribute instance-attribute

iof_hydro__30: Optional[int] = Field(0, description='S @prism centers [PSU] {salinityAtElement} 3D')

iof_hydro__31 class-attribute instance-attribute

iof_hydro__31: Optional[int] = Field(0, description='Barotropic pressure gradient force vector (m.s-2) @side centers  {pressure_gradient} 2D vector')

iof_wwm__1 class-attribute instance-attribute

iof_wwm__1: Optional[int] = Field(0, description='sig. height (m) {sigWaveHeight}   2D')

iof_wwm__2 class-attribute instance-attribute

iof_wwm__2: Optional[int] = Field(0, description='Mean average period (sec) - TM01 {meanWavePeriod}  2D')

iof_wwm__3 class-attribute instance-attribute

iof_wwm__3: Optional[int] = Field(0, description='Zero down crossing period for comparison with buoy (s) - TM02 {zeroDowncrossPeriod}  2D')

iof_wwm__4 class-attribute instance-attribute

iof_wwm__4: Optional[int] = Field(0, description='Average period of wave runup/overtopping - TM10 {TM10}  2D')

iof_wwm__5 class-attribute instance-attribute

iof_wwm__5: Optional[int] = Field(0, description='Mean wave number (1/m) {meanWaveNumber}  2D')

iof_wwm__6 class-attribute instance-attribute

iof_wwm__6: Optional[int] = Field(0, description='Mean wave length (m) {meanWaveLength}  2D')

iof_wwm__7 class-attribute instance-attribute

iof_wwm__7: Optional[int] = Field(0, description='Mean average energy transport direction (degr) - MWD in NDBC? {meanWaveDirection}  2D')

iof_wwm__8 class-attribute instance-attribute

iof_wwm__8: Optional[int] = Field(0, description='Mean directional spreading (degr) {meanDirSpreading}  2D')

iof_wwm__9 class-attribute instance-attribute

iof_wwm__9: Optional[int] = Field(0, description='Discrete peak period (sec) - Tp {peakPeriod}  2D')

iof_wwm__10 class-attribute instance-attribute

iof_wwm__10: Optional[int] = Field(0, description='Continuous peak period based on higher order moments (sec) {continuousPeakPeriod}  2D')

iof_wwm__11 class-attribute instance-attribute

iof_wwm__11: Optional[int] = Field(0, description='Peak phase vel. (m/s) {peakPhaseVel}  2D')

iof_wwm__12 class-attribute instance-attribute

iof_wwm__12: Optional[int] = Field(0, description='Peak n-factor {peakNFactor}   2D')

iof_wwm__13 class-attribute instance-attribute

iof_wwm__13: Optional[int] = Field(0, description='Peak group vel. (m/s) {peakGroupVel}   2D')

iof_wwm__14 class-attribute instance-attribute

iof_wwm__14: Optional[int] = Field(0, description='Peak wave number {peakWaveNumber}  2D')

iof_wwm__15 class-attribute instance-attribute

iof_wwm__15: Optional[int] = Field(0, description='Peak wave length {peakWaveLength}  2D')

iof_wwm__16 class-attribute instance-attribute

iof_wwm__16: Optional[int] = Field(0, description='Peak (dominant) direction (degr) {dominantDirection}  2D')

iof_wwm__17 class-attribute instance-attribute

iof_wwm__17: Optional[int] = Field(0, description='Peak directional spreading {peakSpreading}  2D')

iof_wwm__18 class-attribute instance-attribute

iof_wwm__18: Optional[int] = Field(0, description='Discrete peak direction (radian?) {discretePeakDirectio}  2D')

iof_wwm__19 class-attribute instance-attribute

iof_wwm__19: Optional[int] = Field(0, description='Orbital vel. (m/s) {orbitalVelocity}  2D')

iof_wwm__20 class-attribute instance-attribute

iof_wwm__20: Optional[int] = Field(0, description='RMS Orbital vel. (m/s) {rmsOrbitalVelocity}  2D')

iof_wwm__21 class-attribute instance-attribute

iof_wwm__21: Optional[int] = Field(0, description='Bottom excursion period (sec?) {bottomExcursionPerio}  2D')

iof_wwm__22 class-attribute instance-attribute

iof_wwm__22: Optional[int] = Field(0, description='Bottom wave period (sec) {bottomWavePeriod}  2D')

iof_wwm__23 class-attribute instance-attribute

iof_wwm__23: Optional[int] = Field(0, description='Uresell number based on peak period {UresellNumber}  2D')

iof_wwm__24 class-attribute instance-attribute

iof_wwm__24: Optional[int] = Field(0, description='Friction velocity (m/s?) {frictionalVelocity}  2D')

iof_wwm__25 class-attribute instance-attribute

iof_wwm__25: Optional[int] = Field(0, description='Charnock coefficient {CharnockCoeff}  2D')

iof_wwm__26 class-attribute instance-attribute

iof_wwm__26: Optional[int] = Field(0, description='Rougness length {rougnessLength}  2D')

iof_wwm__27 class-attribute instance-attribute

iof_wwm__27: Optional[int] = Field(0, description='Roller energy dissipation rate (W/m²) @nodes {Drol} 2D')

iof_wwm__28 class-attribute instance-attribute

iof_wwm__28: Optional[int] = Field(0, description='Total wave energy dissipation rate by depth-induced breaking (W/m²) @nodes {wave_sbrtot}  2D')

iof_wwm__29 class-attribute instance-attribute

iof_wwm__29: Optional[int] = Field(0, description='Total wave energy dissipation rate by bottom friction (W/m²) @nodes {wave_sbftot} 2D')

iof_wwm__30 class-attribute instance-attribute

iof_wwm__30: Optional[int] = Field(0, description='Total wave energy dissipation rate by whitecapping (W/m²) @nodes {wave_sdstot} 2D')

iof_wwm__31 class-attribute instance-attribute

iof_wwm__31: Optional[int] = Field(0, description='Total wave energy dissipation rate by vegetation (W/m²) @nodes {wave_svegtot} 2D')

iof_wwm__32 class-attribute instance-attribute

iof_wwm__32: Optional[int] = Field(0, description='Total wave energy input rate from atmospheric forcing (W/m²) @nodes {wave_sintot} 2D')

iof_wwm__33 class-attribute instance-attribute

iof_wwm__33: Optional[int] = Field(0, description='WWM_energy vector {waveEnergyDirX,Y}  2D vector')

iof_wwm__34 class-attribute instance-attribute

iof_wwm__34: Optional[int] = Field(0, description='Vertical Stokes velocity (m.s-1) @sides and whole levels {stokes_wvel}  3D')

iof_wwm__35 class-attribute instance-attribute

iof_wwm__35: Optional[int] = Field(0, description='Wave force vector (m.s-2) computed by wwm @side centers and whole levels {waveForceX,Y}   3D vector')

iof_wwm__36 class-attribute instance-attribute

iof_wwm__36: Optional[int] = Field(0, description='Horizontal Stokes velocity (m.s-1) @nodes and whole levels {stokes_hvel} 3D vector')

iof_wwm__37 class-attribute instance-attribute

iof_wwm__37: Optional[int] = Field(0, description='Roller contribution to horizontal Stokes velocity (m.s-1) @nodes and whole levels {roller_stokes_hvel} 3D vector')

Functions

validate_nc_out classmethod

validate_nc_out(v)
Source code in rompy_schism/namelists/param.py
@field_validator("nc_out")
@classmethod
def validate_nc_out(cls, v):
    if v not in [0, 1]:
        raise ValueError("nc_out must be 0 or 1")
    return v

validate_iof_ugrid classmethod

validate_iof_ugrid(v)
Source code in rompy_schism/namelists/param.py
@field_validator("iof_ugrid")
@classmethod
def validate_iof_ugrid(cls, v):
    if v < 0:
        raise ValueError("iof_ugrid must be non-negative")
    return v

validate_nhot classmethod

validate_nhot(v)
Source code in rompy_schism/namelists/param.py
@field_validator("nhot")
@classmethod
def validate_nhot(cls, v):
    if v not in [0, 1]:
        raise ValueError("nhot must be 0 or 1")
    return v

validate_nhot_write classmethod

validate_nhot_write(v)
Source code in rompy_schism/namelists/param.py
@field_validator("nhot_write")
@classmethod
def validate_nhot_write(cls, v):
    if v <= 0:
        raise ValueError("nhot_write must be positive")
    return v

validate_iout_sta classmethod

validate_iout_sta(v)
Source code in rompy_schism/namelists/param.py
@field_validator("iout_sta")
@classmethod
def validate_iout_sta(cls, v):
    if v < 0:
        raise ValueError("iout_sta must be non-negative")
    return v

validate_nspool_sta classmethod

validate_nspool_sta(v)
Source code in rompy_schism/namelists/param.py
@field_validator("nspool_sta")
@classmethod
def validate_nspool_sta(cls, v):
    if v <= 0:
        raise ValueError("nspool_sta must be positive")
    return v

validate_nhot_write_multiple

validate_nhot_write_multiple()
Source code in rompy_schism/namelists/param.py
@model_validator(mode="after")
def validate_nhot_write_multiple(self):
    if self.nhot == 1:
        if self.nhot_write % self.ihfskip != 0:
            raise ValueError("nhot_write must be a multiple of ihfskip when nhot=1")
        if self.ihfskip % self.dt != 0:
            raise ValueError("ihfskip must be a multiple of dt")
    return self

validate_nspool_sta_requirement

validate_nspool_sta_requirement()
Source code in rompy_schism/namelists/param.py
@model_validator(mode="after")
def validate_nspool_sta_requirement(self):
    if self.iout_sta != 0 and self.nspool_sta <= 0:
        raise ValueError("nspool_sta must be positive when iout_sta is non-zero")
    return self

Vertical

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/param.py
class Vertical(NamelistBaseModel):
    vnh1: Optional[int] = Field(
        400,
        description="Vertical nudging depth 1 in meters. Used in vertical relaxation scheme.",
    )
    vnf1: Optional[float] = Field(
        0.0,
        description="Vertical relaxation factor for depth 1. Must be between 0 and 1.",
    )
    vnh2: Optional[int] = Field(
        500,
        description="Vertical nudging depth 2 in meters. Must be greater than vnh1.",
    )
    vnf2: Optional[float] = Field(
        0.0,
        description="Vertical relaxation factor for depth 2. Must be between 0 and 1.",
    )
    step_nu_tr: Optional[float] = Field(
        86400.0,
        description="Time step in seconds for all *_nu.nc files when inu_[MOD]=2.",
    )
    h_bcc1: Optional[float] = Field(
        100.0,
        description="Cut-off depth for cubic spline interpolation near bottom when computing horizontal gradients.",
    )
    s1_mxnbt: Optional[float] = Field(
        0.5, description="Dimensioning parameter for inter-subdomain backtracking."
    )
    s2_mxnbt: Optional[float] = Field(
        3.5,
        description="Another dimensioning parameter for inter-subdomain backtracking.",
    )
    iharind: Optional[int] = Field(
        0,
        description="Flag for harmonic analysis of elevation. 0 for off, non-zero for on.",
    )
    iflux: Optional[int] = Field(
        0,
        description="Conservation check option. 0: off, 1: basic output, 2: more elaborate outputs.",
    )
    izonal5: Optional[int] = Field(
        0,
        description="Flag for Williamson test #5 (zonal flow over an isolated mount). 0 for off, non-zero for on.",
    )
    ibtrack_test: Optional[int] = Field(
        0,
        description="Flag for rotating Gausshill test with stratified T,S. 0: off, 1: on.",
    )
    irouse_test: Optional[int] = Field(
        0,
        description="Flag for Rouse profile test. 0: off, 1: on. Requires USE_TIMOR to be on if enabled.",
    )
    flag_fib: Optional[int] = Field(
        1,
        description="Flag to choose FIB model for bacteria decay. 1: Constant decay rate, 2: Canteras et al., 1995, 3: Servais et al., 2007.",
    )
    slr_rate: Optional[float] = Field(
        120.0,
        description="Sea-level rise rate in mm/year for marsh model. Only used if USE_MARSH is on.",
    )
    nstep_ice: Optional[int] = Field(
        1, description="Number of SCHISM steps between calls to the ICE module."
    )
    rearth_pole: Optional[float] = Field(
        6378206.4, description="Earth's radius at the pole in meters."
    )
    rearth_eq: Optional[float] = Field(
        6378206.4, description="Earth's radius at the equator in meters."
    )
    shw: Optional[float] = Field(
        4184.0, description="Specific heat of water (C_p) in J/kg/K."
    )
    rho0: Optional[float] = Field(
        1000.0,
        description="Reference water density for Boussinesq approximation in kg/m^3.",
    )
    vclose_surf_frac: Optional[float] = Field(
        1.0,
        description="Fraction of vertical flux closure adjustment applied at surface for T,S.",
    )
    iadjust_mass_consv0__1: Optional[int] = Field(0, description="T")
    iadjust_mass_consv0__2: Optional[int] = Field(0, description="S")
    iadjust_mass_consv0__3: Optional[int] = Field(0, description="GEN")
    iadjust_mass_consv0__4: Optional[int] = Field(0, description="AGE")
    iadjust_mass_consv0__5: Optional[int] = Field(
        0, description="SED3D (code won't allow non-0 for this module)"
    )
    iadjust_mass_consv0__6: Optional[int] = Field(0, description="EcoSim")
    iadjust_mass_consv0__7: Optional[int] = Field(0, description="ICM")
    iadjust_mass_consv0__8: Optional[int] = Field(0, description="CoSiNE")
    iadjust_mass_consv0__9: Optional[int] = Field(0, description="Feco")
    iadjust_mass_consv0__10: Optional[int] = Field(0, description="TIMOR")
    iadjust_mass_consv0__11: Optional[int] = Field(0, description="FABM")
    iadjust_mass_consv0__12: Optional[int] = Field(0, description="DVD (must=0)")
    h_massconsv: Optional[float] = Field(
        2.0, description="Threshold depth for ICM mass conservation in meters."
    )
    rinflation_icm: Optional[float] = Field(
        0.001,
        description="Maximum ratio between H^{n+1} and H^n allowed for ICM mass conservation.",
    )

    @field_validator("vnh1")
    @classmethod
    def check_vnh1(cls, v):
        if v <= 0:
            raise ValueError("vnh1 must be positive")
        return v

    @field_validator("vnf1")
    @classmethod
    def check_vnf1(cls, v):
        if not 0 <= v <= 1:
            raise ValueError("vnf1 must be between 0 and 1")
        return v

    @field_validator("vnh2")
    @classmethod
    def check_vnh2(cls, v):
        if v <= 0:
            raise ValueError("vnh2 must be positive")
        return v

    @field_validator("vnf2")
    @classmethod
    def check_vnf2(cls, v):
        if not 0 <= v <= 1:
            raise ValueError("vnf2 must be between 0 and 1")
        return v

    @field_validator("step_nu_tr")
    @classmethod
    def check_step_nu_tr(cls, v):
        if v <= 0:
            raise ValueError("step_nu_tr must be positive")
        return v

    @field_validator("h_bcc1")
    @classmethod
    def check_h_bcc1(cls, v):
        if v <= 0:
            raise ValueError("h_bcc1 must be positive")
        return v

    @field_validator("s1_mxnbt")
    @classmethod
    def check_s1_mxnbt(cls, v):
        if v <= 0:
            raise ValueError("s1_mxnbt must be positive")
        return v

    @field_validator("s2_mxnbt")
    @classmethod
    def check_s2_mxnbt(cls, v):
        if v <= 0:
            raise ValueError("s2_mxnbt must be positive")
        return v

    @field_validator("iharind")
    @classmethod
    def check_iharind(cls, v):
        if not isinstance(v, int):
            raise ValueError("iharind must be an integer")
        return v

    @field_validator("iflux")
    @classmethod
    def check_iflux(cls, v):
        if v not in [0, 1, 2]:
            raise ValueError("iflux must be 0, 1, or 2")
        return v

    @field_validator("izonal5")
    @classmethod
    def check_izonal5(cls, v):
        if not isinstance(v, int):
            raise ValueError("izonal5 must be an integer")
        return v

    @field_validator("ibtrack_test")
    @classmethod
    def check_ibtrack_test(cls, v):
        if v not in [0, 1]:
            raise ValueError("ibtrack_test must be 0 or 1")
        return v

    @field_validator("irouse_test")
    @classmethod
    def check_irouse_test(cls, v):
        if v not in [0, 1]:
            raise ValueError("irouse_test must be 0 or 1")
        return v

    @field_validator("flag_fib")
    @classmethod
    def check_flag_fib(cls, v):
        if v not in [1, 2, 3]:
            raise ValueError("flag_fib must be 1, 2, or 3")
        return v

    @field_validator("slr_rate")
    @classmethod
    def check_slr_rate(cls, v):
        if v < 0:
            raise ValueError("slr_rate must be non-negative")
        return v

    @field_validator("nstep_ice")
    @classmethod
    def check_nstep_ice(cls, v):
        if v <= 0:
            raise ValueError("nstep_ice must be positive")
        return v

    @field_validator("rearth_pole")
    @classmethod
    def check_rearth_pole(cls, v):
        if v <= 0:
            raise ValueError("rearth_pole must be positive")
        return v

    @field_validator("rearth_eq")
    @classmethod
    def check_rearth_eq(cls, v):
        if v <= 0:
            raise ValueError("rearth_eq must be positive")
        return v

    @field_validator("shw")
    @classmethod
    def check_shw(cls, v):
        # if float(v.replace("d", "")) <= 0:
        if v <= 0:
            raise ValueError("shw must be positive")
        return v

    @field_validator("rho0")
    @classmethod
    def check_rho0(cls, v):
        if v <= 900:
            # if float(v.replace("d", "")) <= 0:
            raise ValueError("rho0 must be positive")
        return v

    @field_validator("vclose_surf_frac")
    @classmethod
    def check_vclose_surf_frac(cls, v):
        if not 0 <= v <= 1:
            raise ValueError("vclose_surf_frac must be between 0 and 1")
        return v

    @field_validator("h_massconsv")
    @classmethod
    def check_h_massconsv(cls, v):
        if v <= 0:
            raise ValueError("h_massconsv must be positive")
        return v

    @field_validator("rinflation_icm")
    @classmethod
    def check_rinflation_icm(cls, v):
        if v <= 0:
            raise ValueError("rinflation_icm must be positive")
        return v

    @model_validator(mode="after")
    def check_vnh2_greater_than_vnh1(self):
        if self.vnh2 <= self.vnh1:
            raise ValueError("vnh2 must be greater than vnh1")
        return self

Attributes

vnh1 class-attribute instance-attribute

vnh1: Optional[int] = Field(400, description='Vertical nudging depth 1 in meters. Used in vertical relaxation scheme.')

vnf1 class-attribute instance-attribute

vnf1: Optional[float] = Field(0.0, description='Vertical relaxation factor for depth 1. Must be between 0 and 1.')

vnh2 class-attribute instance-attribute

vnh2: Optional[int] = Field(500, description='Vertical nudging depth 2 in meters. Must be greater than vnh1.')

vnf2 class-attribute instance-attribute

vnf2: Optional[float] = Field(0.0, description='Vertical relaxation factor for depth 2. Must be between 0 and 1.')

step_nu_tr class-attribute instance-attribute

step_nu_tr: Optional[float] = Field(86400.0, description='Time step in seconds for all *_nu.nc files when inu_[MOD]=2.')

h_bcc1 class-attribute instance-attribute

h_bcc1: Optional[float] = Field(100.0, description='Cut-off depth for cubic spline interpolation near bottom when computing horizontal gradients.')

s1_mxnbt class-attribute instance-attribute

s1_mxnbt: Optional[float] = Field(0.5, description='Dimensioning parameter for inter-subdomain backtracking.')

s2_mxnbt class-attribute instance-attribute

s2_mxnbt: Optional[float] = Field(3.5, description='Another dimensioning parameter for inter-subdomain backtracking.')

iharind class-attribute instance-attribute

iharind: Optional[int] = Field(0, description='Flag for harmonic analysis of elevation. 0 for off, non-zero for on.')

iflux class-attribute instance-attribute

iflux: Optional[int] = Field(0, description='Conservation check option. 0: off, 1: basic output, 2: more elaborate outputs.')

izonal5 class-attribute instance-attribute

izonal5: Optional[int] = Field(0, description='Flag for Williamson test #5 (zonal flow over an isolated mount). 0 for off, non-zero for on.')

ibtrack_test class-attribute instance-attribute

ibtrack_test: Optional[int] = Field(0, description='Flag for rotating Gausshill test with stratified T,S. 0: off, 1: on.')

irouse_test class-attribute instance-attribute

irouse_test: Optional[int] = Field(0, description='Flag for Rouse profile test. 0: off, 1: on. Requires USE_TIMOR to be on if enabled.')

flag_fib class-attribute instance-attribute

flag_fib: Optional[int] = Field(1, description='Flag to choose FIB model for bacteria decay. 1: Constant decay rate, 2: Canteras et al., 1995, 3: Servais et al., 2007.')

slr_rate class-attribute instance-attribute

slr_rate: Optional[float] = Field(120.0, description='Sea-level rise rate in mm/year for marsh model. Only used if USE_MARSH is on.')

nstep_ice class-attribute instance-attribute

nstep_ice: Optional[int] = Field(1, description='Number of SCHISM steps between calls to the ICE module.')

rearth_pole class-attribute instance-attribute

rearth_pole: Optional[float] = Field(6378206.4, description="Earth's radius at the pole in meters.")

rearth_eq class-attribute instance-attribute

rearth_eq: Optional[float] = Field(6378206.4, description="Earth's radius at the equator in meters.")

shw class-attribute instance-attribute

shw: Optional[float] = Field(4184.0, description='Specific heat of water (C_p) in J/kg/K.')

rho0 class-attribute instance-attribute

rho0: Optional[float] = Field(1000.0, description='Reference water density for Boussinesq approximation in kg/m^3.')

vclose_surf_frac class-attribute instance-attribute

vclose_surf_frac: Optional[float] = Field(1.0, description='Fraction of vertical flux closure adjustment applied at surface for T,S.')

iadjust_mass_consv0__1 class-attribute instance-attribute

iadjust_mass_consv0__1: Optional[int] = Field(0, description='T')

iadjust_mass_consv0__2 class-attribute instance-attribute

iadjust_mass_consv0__2: Optional[int] = Field(0, description='S')

iadjust_mass_consv0__3 class-attribute instance-attribute

iadjust_mass_consv0__3: Optional[int] = Field(0, description='GEN')

iadjust_mass_consv0__4 class-attribute instance-attribute

iadjust_mass_consv0__4: Optional[int] = Field(0, description='AGE')

iadjust_mass_consv0__5 class-attribute instance-attribute

iadjust_mass_consv0__5: Optional[int] = Field(0, description="SED3D (code won't allow non-0 for this module)")

iadjust_mass_consv0__6 class-attribute instance-attribute

iadjust_mass_consv0__6: Optional[int] = Field(0, description='EcoSim')

iadjust_mass_consv0__7 class-attribute instance-attribute

iadjust_mass_consv0__7: Optional[int] = Field(0, description='ICM')

iadjust_mass_consv0__8 class-attribute instance-attribute

iadjust_mass_consv0__8: Optional[int] = Field(0, description='CoSiNE')

iadjust_mass_consv0__9 class-attribute instance-attribute

iadjust_mass_consv0__9: Optional[int] = Field(0, description='Feco')

iadjust_mass_consv0__10 class-attribute instance-attribute

iadjust_mass_consv0__10: Optional[int] = Field(0, description='TIMOR')

iadjust_mass_consv0__11 class-attribute instance-attribute

iadjust_mass_consv0__11: Optional[int] = Field(0, description='FABM')

iadjust_mass_consv0__12 class-attribute instance-attribute

iadjust_mass_consv0__12: Optional[int] = Field(0, description='DVD (must=0)')

h_massconsv class-attribute instance-attribute

h_massconsv: Optional[float] = Field(2.0, description='Threshold depth for ICM mass conservation in meters.')

rinflation_icm class-attribute instance-attribute

rinflation_icm: Optional[float] = Field(0.001, description='Maximum ratio between H^{n+1} and H^n allowed for ICM mass conservation.')

Functions

check_vnh1 classmethod

check_vnh1(v)
Source code in rompy_schism/namelists/param.py
@field_validator("vnh1")
@classmethod
def check_vnh1(cls, v):
    if v <= 0:
        raise ValueError("vnh1 must be positive")
    return v

check_vnf1 classmethod

check_vnf1(v)
Source code in rompy_schism/namelists/param.py
@field_validator("vnf1")
@classmethod
def check_vnf1(cls, v):
    if not 0 <= v <= 1:
        raise ValueError("vnf1 must be between 0 and 1")
    return v

check_vnh2 classmethod

check_vnh2(v)
Source code in rompy_schism/namelists/param.py
@field_validator("vnh2")
@classmethod
def check_vnh2(cls, v):
    if v <= 0:
        raise ValueError("vnh2 must be positive")
    return v

check_vnf2 classmethod

check_vnf2(v)
Source code in rompy_schism/namelists/param.py
@field_validator("vnf2")
@classmethod
def check_vnf2(cls, v):
    if not 0 <= v <= 1:
        raise ValueError("vnf2 must be between 0 and 1")
    return v

check_step_nu_tr classmethod

check_step_nu_tr(v)
Source code in rompy_schism/namelists/param.py
@field_validator("step_nu_tr")
@classmethod
def check_step_nu_tr(cls, v):
    if v <= 0:
        raise ValueError("step_nu_tr must be positive")
    return v

check_h_bcc1 classmethod

check_h_bcc1(v)
Source code in rompy_schism/namelists/param.py
@field_validator("h_bcc1")
@classmethod
def check_h_bcc1(cls, v):
    if v <= 0:
        raise ValueError("h_bcc1 must be positive")
    return v

check_s1_mxnbt classmethod

check_s1_mxnbt(v)
Source code in rompy_schism/namelists/param.py
@field_validator("s1_mxnbt")
@classmethod
def check_s1_mxnbt(cls, v):
    if v <= 0:
        raise ValueError("s1_mxnbt must be positive")
    return v

check_s2_mxnbt classmethod

check_s2_mxnbt(v)
Source code in rompy_schism/namelists/param.py
@field_validator("s2_mxnbt")
@classmethod
def check_s2_mxnbt(cls, v):
    if v <= 0:
        raise ValueError("s2_mxnbt must be positive")
    return v

check_iharind classmethod

check_iharind(v)
Source code in rompy_schism/namelists/param.py
@field_validator("iharind")
@classmethod
def check_iharind(cls, v):
    if not isinstance(v, int):
        raise ValueError("iharind must be an integer")
    return v

check_iflux classmethod

check_iflux(v)
Source code in rompy_schism/namelists/param.py
@field_validator("iflux")
@classmethod
def check_iflux(cls, v):
    if v not in [0, 1, 2]:
        raise ValueError("iflux must be 0, 1, or 2")
    return v

check_izonal5 classmethod

check_izonal5(v)
Source code in rompy_schism/namelists/param.py
@field_validator("izonal5")
@classmethod
def check_izonal5(cls, v):
    if not isinstance(v, int):
        raise ValueError("izonal5 must be an integer")
    return v

check_ibtrack_test classmethod

check_ibtrack_test(v)
Source code in rompy_schism/namelists/param.py
@field_validator("ibtrack_test")
@classmethod
def check_ibtrack_test(cls, v):
    if v not in [0, 1]:
        raise ValueError("ibtrack_test must be 0 or 1")
    return v

check_irouse_test classmethod

check_irouse_test(v)
Source code in rompy_schism/namelists/param.py
@field_validator("irouse_test")
@classmethod
def check_irouse_test(cls, v):
    if v not in [0, 1]:
        raise ValueError("irouse_test must be 0 or 1")
    return v

check_flag_fib classmethod

check_flag_fib(v)
Source code in rompy_schism/namelists/param.py
@field_validator("flag_fib")
@classmethod
def check_flag_fib(cls, v):
    if v not in [1, 2, 3]:
        raise ValueError("flag_fib must be 1, 2, or 3")
    return v

check_slr_rate classmethod

check_slr_rate(v)
Source code in rompy_schism/namelists/param.py
@field_validator("slr_rate")
@classmethod
def check_slr_rate(cls, v):
    if v < 0:
        raise ValueError("slr_rate must be non-negative")
    return v

check_nstep_ice classmethod

check_nstep_ice(v)
Source code in rompy_schism/namelists/param.py
@field_validator("nstep_ice")
@classmethod
def check_nstep_ice(cls, v):
    if v <= 0:
        raise ValueError("nstep_ice must be positive")
    return v

check_rearth_pole classmethod

check_rearth_pole(v)
Source code in rompy_schism/namelists/param.py
@field_validator("rearth_pole")
@classmethod
def check_rearth_pole(cls, v):
    if v <= 0:
        raise ValueError("rearth_pole must be positive")
    return v

check_rearth_eq classmethod

check_rearth_eq(v)
Source code in rompy_schism/namelists/param.py
@field_validator("rearth_eq")
@classmethod
def check_rearth_eq(cls, v):
    if v <= 0:
        raise ValueError("rearth_eq must be positive")
    return v

check_shw classmethod

check_shw(v)
Source code in rompy_schism/namelists/param.py
@field_validator("shw")
@classmethod
def check_shw(cls, v):
    # if float(v.replace("d", "")) <= 0:
    if v <= 0:
        raise ValueError("shw must be positive")
    return v

check_rho0 classmethod

check_rho0(v)
Source code in rompy_schism/namelists/param.py
@field_validator("rho0")
@classmethod
def check_rho0(cls, v):
    if v <= 900:
        # if float(v.replace("d", "")) <= 0:
        raise ValueError("rho0 must be positive")
    return v

check_vclose_surf_frac classmethod

check_vclose_surf_frac(v)
Source code in rompy_schism/namelists/param.py
@field_validator("vclose_surf_frac")
@classmethod
def check_vclose_surf_frac(cls, v):
    if not 0 <= v <= 1:
        raise ValueError("vclose_surf_frac must be between 0 and 1")
    return v

check_h_massconsv classmethod

check_h_massconsv(v)
Source code in rompy_schism/namelists/param.py
@field_validator("h_massconsv")
@classmethod
def check_h_massconsv(cls, v):
    if v <= 0:
        raise ValueError("h_massconsv must be positive")
    return v

check_rinflation_icm classmethod

check_rinflation_icm(v)
Source code in rompy_schism/namelists/param.py
@field_validator("rinflation_icm")
@classmethod
def check_rinflation_icm(cls, v):
    if v <= 0:
        raise ValueError("rinflation_icm must be positive")
    return v

check_vnh2_greater_than_vnh1

check_vnh2_greater_than_vnh1()
Source code in rompy_schism/namelists/param.py
@model_validator(mode="after")
def check_vnh2_greater_than_vnh1(self):
    if self.vnh2 <= self.vnh1:
        raise ValueError("vnh2 must be greater than vnh1")
    return self

Param

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/param.py
class Param(NamelistBaseModel):
    core: Optional[Core] = Field(default_factory=Core)
    opt: Optional[Opt] = Field(default_factory=Opt)
    vertical: Optional[Vertical] = Field(default_factory=Vertical)
    vegetation: Optional[Vegetation] = Field(default_factory=Vegetation)
    schout: Optional[Schout] = Field(default_factory=Schout)

Attributes

core class-attribute instance-attribute

core: Optional[Core] = Field(default_factory=Core)

opt class-attribute instance-attribute

opt: Optional[Opt] = Field(default_factory=Opt)

vertical class-attribute instance-attribute

vertical: Optional[Vertical] = Field(default_factory=Vertical)

vegetation class-attribute instance-attribute

vegetation: Optional[Vegetation] = Field(default_factory=Vegetation)

schout class-attribute instance-attribute

schout: Optional[Schout] = Field(default_factory=Schout)

ICE

Ice_in

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/ice.py
class Ice_in(NamelistBaseModel):
    ice_tests: Optional[int] = Field(
        0, description="Flag for box test. 0 disables, 1 enables."
    )
    ice_advection: Optional[int] = Field(
        1, description="Flag to enable/disable ice advection. 1 enables, 0 disables."
    )
    ice_therm_on: Optional[int] = Field(
        1,
        description="Flag to enable/disable ice thermodynamics. 1 enables, 0 disables.",
    )
    ievp: Optional[int] = Field(
        2,
        description="Selects the rheology model. 1 for EVP (Elastic-Viscous-Plastic), 2 for mEVP (modified EVP).",
    )
    ice_cutoff: Optional[float] = Field(
        0.001,
        description="Cut-off thickness [m] or fraction for ice. No ice velocity if ice thickness is less than or equal to ice_cutoff.",
    )
    evp_rheol_steps: Optional[int] = Field(
        200, description="Number of subcycling steps in EVP rheology model."
    )
    mevp_rheol_steps: Optional[int] = Field(
        200, description="Number of iterations in mEVP rheology model."
    )
    ice_atmos_stress_form: Optional[int] = Field(
        1,
        description="Form of atmospheric stress calculation. 0 for constant Cd, 1 for FESOM formulation.",
    )
    cdwin0: Optional[float] = Field(
        0.002,
        description="Constant drag coefficient for wind stress, used if ice_atmos_stress_form=0.",
    )
    delta_min: Optional[float] = Field(
        2e-09,
        description="Limit for minimum divergence (1/s). Used in both VP and EVP rheology models.",
    )
    theta_io: Optional[float] = Field(
        0.0,
        description="Ice/ocean rotation angle in degrees. Usually 0 unless vertical grid is too coarse.",
    )
    mevp_coef: Optional[int] = Field(
        0,
        description="Options for specifying 2 relax coefficients in mEVP. 0 for constant, 1 for variable coefficients.",
    )
    mevp_alpha1: Optional[float] = Field(
        200.0,
        description="Constant used in mEVP for constitutive equation if mevp_coef=0.",
    )
    mevp_alpha2: Optional[float] = Field(
        200.0, description="Constant used in mEVP for momentum equation if mevp_coef=0."
    )
    mevp_alpha3: Optional[float] = Field(
        200.0, description="Minimum value for variable coefficients if mevp_coef=1."
    )
    mevp_alpha4: Optional[float] = Field(
        0.02,
        description="Coefficient used in variable coefficient calculation if mevp_coef=1.",
    )
    pstar: Optional[float] = Field(
        15000.0, description="Ice strength parameter [N/m^2]."
    )
    ellipse: Optional[float] = Field(2.0, description="Ellipticity of the yield curve.")
    c_pressure: Optional[float] = Field(
        20.0, description="Ice concentration parameter C [-]."
    )
    ncyc_fct: Optional[int] = Field(
        1, description="Number of subcycling steps in transport for FCT scheme."
    )
    niter_fct: Optional[int] = Field(
        3, description="Number of iterations in higher-order solve for FCT scheme."
    )
    ice_gamma_fct: Optional[float] = Field(
        0.25,
        description="Smoothing parameter for FCT scheme. 1 for maximum positivity preserving.",
    )
    depth_ice_fct: Optional[float] = Field(
        5.0, description="Cut-off depth (m) for non-FCT zone in ice_fct.gr3."
    )
    h_ml0: Optional[float] = Field(
        0.1, description="Ocean mixed layer depth [m] for thermodynamics calculations."
    )
    salt_ice: Optional[float] = Field(
        5.0, description="Salinity for ice [PSU] (must be non-negative)."
    )
    salt_water: Optional[float] = Field(
        34.0, description="Salinity for water [PSU] (must be non-negative)."
    )
    lead_closing: Optional[float] = Field(
        0.5,
        description="Lead closing parameter [m]. Larger values slow down freezing-up but increase sea ice thickness.",
    )
    saterm: Optional[float] = Field(
        0.5, description="Semter constant. Smaller values could slow down melting."
    )
    albsn: Optional[float] = Field(0.85, description="Albedo for frozen snow.")
    albsnm: Optional[float] = Field(
        0.75,
        description="Albedo for melting snow (must be less than or equal to albsn).",
    )
    albi: Optional[float] = Field(
        0.75, description="Albedo for frozen ice (must be less than or equal to albsn)."
    )
    albm: Optional[float] = Field(
        0.66, description="Albedo for melting ice (must be less than or equal to albi)."
    )

    @field_validator("ice_tests")
    @classmethod
    def validate_ice_tests(cls, v):
        if not isinstance(v, int) or v not in [0, 1]:
            raise ValueError("ice_tests must be 0 or 1")
        return v

    @field_validator("ice_advection")
    @classmethod
    def validate_ice_advection(cls, v):
        if not isinstance(v, int) or v not in [0, 1]:
            raise ValueError("ice_advection must be 0 or 1")
        return v

    @field_validator("ice_therm_on")
    @classmethod
    def validate_ice_therm_on(cls, v):
        if not isinstance(v, int) or v not in [0, 1]:
            raise ValueError("ice_therm_on must be 0 or 1")
        return v

    @field_validator("ievp")
    @classmethod
    def validate_ievp(cls, v):
        if not isinstance(v, int) or v not in [1, 2]:
            raise ValueError("ievp must be 1 or 2")
        return v

    @field_validator("ice_cutoff")
    @classmethod
    def validate_ice_cutoff(cls, v):
        if not isinstance(v, float) or v <= 0:
            raise ValueError("ice_cutoff must be a positive float")
        return v

    @field_validator("evp_rheol_steps")
    @classmethod
    def validate_evp_rheol_steps(cls, v):
        if not isinstance(v, int) or v <= 0:
            raise ValueError("evp_rheol_steps must be a positive integer")
        return v

    @field_validator("mevp_rheol_steps")
    @classmethod
    def validate_mevp_rheol_steps(cls, v):
        if not isinstance(v, int) or v <= 0:
            raise ValueError("mevp_rheol_steps must be a positive integer")
        return v

    @field_validator("ice_atmos_stress_form")
    @classmethod
    def validate_ice_atmos_stress_form(cls, v):
        if not isinstance(v, int) or v not in [0, 1]:
            raise ValueError("ice_atmos_stress_form must be 0 or 1")
        return v

    @field_validator("cdwin0")
    @classmethod
    def validate_cdwin0(cls, v):
        if not isinstance(v, float) or v <= 0:
            raise ValueError("cdwin0 must be a positive float")
        return v

    @field_validator("delta_min")
    @classmethod
    def validate_delta_min(cls, v):
        if not isinstance(v, float) or v <= 0:
            raise ValueError("delta_min must be a positive float")
        return v

    @field_validator("theta_io")
    @classmethod
    def validate_theta_io(cls, v):
        if not isinstance(v, float) or v < 0 or v >= 360:
            raise ValueError("theta_io must be a float between 0 and 360")
        return v

    @field_validator("mevp_coef")
    @classmethod
    def validate_mevp_coef(cls, v):
        if not isinstance(v, int) or v not in [0, 1]:
            raise ValueError("mevp_coef must be 0 or 1")
        return v

    @field_validator("mevp_alpha1")
    @classmethod
    def validate_mevp_alpha1(cls, v):
        if not isinstance(v, float) or v <= 0:
            raise ValueError("mevp_alpha1 must be a positive float")
        return v

    @field_validator("mevp_alpha2")
    @classmethod
    def validate_mevp_alpha2(cls, v):
        if not isinstance(v, float) or v <= 0:
            raise ValueError("mevp_alpha2 must be a positive float")
        return v

    @field_validator("mevp_alpha3")
    @classmethod
    def validate_mevp_alpha3(cls, v):
        if not isinstance(v, float) or v <= 0:
            raise ValueError("mevp_alpha3 must be a positive float")
        return v

    @field_validator("mevp_alpha4")
    @classmethod
    def validate_mevp_alpha4(cls, v):
        if not isinstance(v, float) or v <= 0:
            raise ValueError("mevp_alpha4 must be a positive float")
        return v

    @field_validator("pstar")
    @classmethod
    def validate_pstar(cls, v):
        if not isinstance(v, float) or v <= 0:
            raise ValueError("pstar must be a positive float")
        return v

    @field_validator("ellipse")
    @classmethod
    def validate_ellipse(cls, v):
        if not isinstance(v, float) or v <= 1:
            raise ValueError("ellipse must be a float greater than 1")
        return v

    @field_validator("c_pressure")
    @classmethod
    def validate_c_pressure(cls, v):
        if not isinstance(v, float) or v <= 0:
            raise ValueError("c_pressure must be a positive float")
        return v

    @field_validator("ncyc_fct")
    @classmethod
    def validate_ncyc_fct(cls, v):
        if not isinstance(v, int) or v <= 0:
            raise ValueError("ncyc_fct must be a positive integer")
        return v

    @field_validator("niter_fct")
    @classmethod
    def validate_niter_fct(cls, v):
        if not isinstance(v, int) or v <= 0:
            raise ValueError("niter_fct must be a positive integer")
        return v

    @field_validator("ice_gamma_fct")
    @classmethod
    def validate_ice_gamma_fct(cls, v):
        if not isinstance(v, float) or v < 0 or v > 1:
            raise ValueError("ice_gamma_fct must be a float between 0 and 1")
        return v

    @field_validator("depth_ice_fct")
    @classmethod
    def validate_depth_ice_fct(cls, v):
        if not isinstance(v, float) or v <= 0:
            raise ValueError("depth_ice_fct must be a positive float")
        return v

    @field_validator("h_ml0")
    @classmethod
    def validate_h_ml0(cls, v):
        if not isinstance(v, float) or v <= 0:
            raise ValueError("h_ml0 must be a positive float")
        return v

    @field_validator("salt_ice")
    @classmethod
    def validate_salt_ice(cls, v):
        if not isinstance(v, float) or v < 0:
            raise ValueError("salt_ice must be a non-negative float")
        return v

    @field_validator("salt_water")
    @classmethod
    def validate_salt_water(cls, v):
        if not isinstance(v, float) or v < 0:
            raise ValueError("salt_water must be a non-negative float")
        return v

    @field_validator("lead_closing")
    @classmethod
    def validate_lead_closing(cls, v):
        if not isinstance(v, float) or v <= 0:
            raise ValueError("lead_closing must be a positive float")
        return v

    @field_validator("saterm")
    @classmethod
    def validate_saterm(cls, v):
        if not isinstance(v, float) or v <= 0 or v > 1:
            raise ValueError("saterm must be a float between 0 and 1")
        return v

    @field_validator("albsn")
    @classmethod
    def validate_albsn(cls, v):
        if not isinstance(v, float) or v <= 0 or v > 1:
            raise ValueError("albsn must be a float between 0 and 1")
        return v

    @field_validator("albsnm")
    @classmethod
    def validate_albsnm(cls, v):
        if not isinstance(v, float) or v <= 0 or v > 1:
            raise ValueError("albsnm must be a float between 0 and 1")
        return v

    @field_validator("albi")
    @classmethod
    def validate_albi(cls, v):
        if not isinstance(v, float) or v <= 0 or v > 1:
            raise ValueError("albi must be a float between 0 and 1")
        return v

    @field_validator("albm")
    @classmethod
    def validate_albm(cls, v):
        if not isinstance(v, float) or v <= 0 or v > 1:
            raise ValueError("albm must be a float between 0 and 1")
        return v

    @model_validator(mode="after")
    def validate_rheol_steps(self):
        if self.ievp == 1 and self.evp_rheol_steps <= 0:
            raise ValueError("evp_rheol_steps must be positive when ievp is 1")
        return self

    @model_validator(mode="after")
    def validate_rheol_steps(self):
        if self.ievp == 2 and self.mevp_rheol_steps <= 0:
            raise ValueError("mevp_rheol_steps must be positive when ievp is 2")
        return self

    @model_validator(mode="after")
    def validate_cdwin0_usage(self):
        if self.ice_atmos_stress_form == 0 and self.cdwin0 <= 0:
            raise ValueError("cdwin0 must be positive when ice_atmos_stress_form is 0")
        return self

    @model_validator(mode="after")
    def validate_mevp_alpha1_usage(self):
        if self.mevp_coef == 0 and self.mevp_alpha1 <= 0:
            raise ValueError("mevp_alpha1 must be positive when mevp_coef is 0")
        return self

    @model_validator(mode="after")
    def validate_mevp_alpha2_usage(self):
        if self.mevp_coef == 0 and self.mevp_alpha2 <= 0:
            raise ValueError("mevp_alpha2 must be positive when mevp_coef is 0")
        return self

    @model_validator(mode="after")
    def validate_mevp_alpha3_usage(self):
        if self.mevp_coef == 1 and self.mevp_alpha3 <= 0:
            raise ValueError("mevp_alpha3 must be positive when mevp_coef is 1")
        return self

    @model_validator(mode="after")
    def validate_mevp_alpha4_usage(self):
        if self.mevp_coef == 1 and self.mevp_alpha4 <= 0:
            raise ValueError("mevp_alpha4 must be positive when mevp_coef is 1")
        return self

    @model_validator(mode="after")
    def validate_albsnm_relation(self):
        if self.albsnm > self.albsn:
            raise ValueError("albsnm must be less than or equal to albsn")
        return self

    @model_validator(mode="after")
    def validate_albi_relation(self):
        if self.albi > self.albsn:
            raise ValueError("albi must be less than or equal to albsn")
        return self

    @model_validator(mode="after")
    def validate_albm_relation(self):
        if self.albm > self.albi:
            raise ValueError("albm must be less than or equal to albi")
        return self

Attributes

ice_tests class-attribute instance-attribute

ice_tests: Optional[int] = Field(0, description='Flag for box test. 0 disables, 1 enables.')

ice_advection class-attribute instance-attribute

ice_advection: Optional[int] = Field(1, description='Flag to enable/disable ice advection. 1 enables, 0 disables.')

ice_therm_on class-attribute instance-attribute

ice_therm_on: Optional[int] = Field(1, description='Flag to enable/disable ice thermodynamics. 1 enables, 0 disables.')

ievp class-attribute instance-attribute

ievp: Optional[int] = Field(2, description='Selects the rheology model. 1 for EVP (Elastic-Viscous-Plastic), 2 for mEVP (modified EVP).')

ice_cutoff class-attribute instance-attribute

ice_cutoff: Optional[float] = Field(0.001, description='Cut-off thickness [m] or fraction for ice. No ice velocity if ice thickness is less than or equal to ice_cutoff.')

evp_rheol_steps class-attribute instance-attribute

evp_rheol_steps: Optional[int] = Field(200, description='Number of subcycling steps in EVP rheology model.')

mevp_rheol_steps class-attribute instance-attribute

mevp_rheol_steps: Optional[int] = Field(200, description='Number of iterations in mEVP rheology model.')

ice_atmos_stress_form class-attribute instance-attribute

ice_atmos_stress_form: Optional[int] = Field(1, description='Form of atmospheric stress calculation. 0 for constant Cd, 1 for FESOM formulation.')

cdwin0 class-attribute instance-attribute

cdwin0: Optional[float] = Field(0.002, description='Constant drag coefficient for wind stress, used if ice_atmos_stress_form=0.')

delta_min class-attribute instance-attribute

delta_min: Optional[float] = Field(2e-09, description='Limit for minimum divergence (1/s). Used in both VP and EVP rheology models.')

theta_io class-attribute instance-attribute

theta_io: Optional[float] = Field(0.0, description='Ice/ocean rotation angle in degrees. Usually 0 unless vertical grid is too coarse.')

mevp_coef class-attribute instance-attribute

mevp_coef: Optional[int] = Field(0, description='Options for specifying 2 relax coefficients in mEVP. 0 for constant, 1 for variable coefficients.')

mevp_alpha1 class-attribute instance-attribute

mevp_alpha1: Optional[float] = Field(200.0, description='Constant used in mEVP for constitutive equation if mevp_coef=0.')

mevp_alpha2 class-attribute instance-attribute

mevp_alpha2: Optional[float] = Field(200.0, description='Constant used in mEVP for momentum equation if mevp_coef=0.')

mevp_alpha3 class-attribute instance-attribute

mevp_alpha3: Optional[float] = Field(200.0, description='Minimum value for variable coefficients if mevp_coef=1.')

mevp_alpha4 class-attribute instance-attribute

mevp_alpha4: Optional[float] = Field(0.02, description='Coefficient used in variable coefficient calculation if mevp_coef=1.')

pstar class-attribute instance-attribute

pstar: Optional[float] = Field(15000.0, description='Ice strength parameter [N/m^2].')

ellipse class-attribute instance-attribute

ellipse: Optional[float] = Field(2.0, description='Ellipticity of the yield curve.')

c_pressure class-attribute instance-attribute

c_pressure: Optional[float] = Field(20.0, description='Ice concentration parameter C [-].')

ncyc_fct class-attribute instance-attribute

ncyc_fct: Optional[int] = Field(1, description='Number of subcycling steps in transport for FCT scheme.')

niter_fct class-attribute instance-attribute

niter_fct: Optional[int] = Field(3, description='Number of iterations in higher-order solve for FCT scheme.')

ice_gamma_fct class-attribute instance-attribute

ice_gamma_fct: Optional[float] = Field(0.25, description='Smoothing parameter for FCT scheme. 1 for maximum positivity preserving.')

depth_ice_fct class-attribute instance-attribute

depth_ice_fct: Optional[float] = Field(5.0, description='Cut-off depth (m) for non-FCT zone in ice_fct.gr3.')

h_ml0 class-attribute instance-attribute

h_ml0: Optional[float] = Field(0.1, description='Ocean mixed layer depth [m] for thermodynamics calculations.')

salt_ice class-attribute instance-attribute

salt_ice: Optional[float] = Field(5.0, description='Salinity for ice [PSU] (must be non-negative).')

salt_water class-attribute instance-attribute

salt_water: Optional[float] = Field(34.0, description='Salinity for water [PSU] (must be non-negative).')

lead_closing class-attribute instance-attribute

lead_closing: Optional[float] = Field(0.5, description='Lead closing parameter [m]. Larger values slow down freezing-up but increase sea ice thickness.')

saterm class-attribute instance-attribute

saterm: Optional[float] = Field(0.5, description='Semter constant. Smaller values could slow down melting.')

albsn class-attribute instance-attribute

albsn: Optional[float] = Field(0.85, description='Albedo for frozen snow.')

albsnm class-attribute instance-attribute

albsnm: Optional[float] = Field(0.75, description='Albedo for melting snow (must be less than or equal to albsn).')

albi class-attribute instance-attribute

albi: Optional[float] = Field(0.75, description='Albedo for frozen ice (must be less than or equal to albsn).')

albm class-attribute instance-attribute

albm: Optional[float] = Field(0.66, description='Albedo for melting ice (must be less than or equal to albi).')

Functions

validate_ice_tests classmethod

validate_ice_tests(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("ice_tests")
@classmethod
def validate_ice_tests(cls, v):
    if not isinstance(v, int) or v not in [0, 1]:
        raise ValueError("ice_tests must be 0 or 1")
    return v

validate_ice_advection classmethod

validate_ice_advection(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("ice_advection")
@classmethod
def validate_ice_advection(cls, v):
    if not isinstance(v, int) or v not in [0, 1]:
        raise ValueError("ice_advection must be 0 or 1")
    return v

validate_ice_therm_on classmethod

validate_ice_therm_on(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("ice_therm_on")
@classmethod
def validate_ice_therm_on(cls, v):
    if not isinstance(v, int) or v not in [0, 1]:
        raise ValueError("ice_therm_on must be 0 or 1")
    return v

validate_ievp classmethod

validate_ievp(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("ievp")
@classmethod
def validate_ievp(cls, v):
    if not isinstance(v, int) or v not in [1, 2]:
        raise ValueError("ievp must be 1 or 2")
    return v

validate_ice_cutoff classmethod

validate_ice_cutoff(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("ice_cutoff")
@classmethod
def validate_ice_cutoff(cls, v):
    if not isinstance(v, float) or v <= 0:
        raise ValueError("ice_cutoff must be a positive float")
    return v

validate_evp_rheol_steps classmethod

validate_evp_rheol_steps(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("evp_rheol_steps")
@classmethod
def validate_evp_rheol_steps(cls, v):
    if not isinstance(v, int) or v <= 0:
        raise ValueError("evp_rheol_steps must be a positive integer")
    return v

validate_mevp_rheol_steps classmethod

validate_mevp_rheol_steps(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("mevp_rheol_steps")
@classmethod
def validate_mevp_rheol_steps(cls, v):
    if not isinstance(v, int) or v <= 0:
        raise ValueError("mevp_rheol_steps must be a positive integer")
    return v

validate_ice_atmos_stress_form classmethod

validate_ice_atmos_stress_form(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("ice_atmos_stress_form")
@classmethod
def validate_ice_atmos_stress_form(cls, v):
    if not isinstance(v, int) or v not in [0, 1]:
        raise ValueError("ice_atmos_stress_form must be 0 or 1")
    return v

validate_cdwin0 classmethod

validate_cdwin0(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("cdwin0")
@classmethod
def validate_cdwin0(cls, v):
    if not isinstance(v, float) or v <= 0:
        raise ValueError("cdwin0 must be a positive float")
    return v

validate_delta_min classmethod

validate_delta_min(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("delta_min")
@classmethod
def validate_delta_min(cls, v):
    if not isinstance(v, float) or v <= 0:
        raise ValueError("delta_min must be a positive float")
    return v

validate_theta_io classmethod

validate_theta_io(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("theta_io")
@classmethod
def validate_theta_io(cls, v):
    if not isinstance(v, float) or v < 0 or v >= 360:
        raise ValueError("theta_io must be a float between 0 and 360")
    return v

validate_mevp_coef classmethod

validate_mevp_coef(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("mevp_coef")
@classmethod
def validate_mevp_coef(cls, v):
    if not isinstance(v, int) or v not in [0, 1]:
        raise ValueError("mevp_coef must be 0 or 1")
    return v

validate_mevp_alpha1 classmethod

validate_mevp_alpha1(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("mevp_alpha1")
@classmethod
def validate_mevp_alpha1(cls, v):
    if not isinstance(v, float) or v <= 0:
        raise ValueError("mevp_alpha1 must be a positive float")
    return v

validate_mevp_alpha2 classmethod

validate_mevp_alpha2(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("mevp_alpha2")
@classmethod
def validate_mevp_alpha2(cls, v):
    if not isinstance(v, float) or v <= 0:
        raise ValueError("mevp_alpha2 must be a positive float")
    return v

validate_mevp_alpha3 classmethod

validate_mevp_alpha3(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("mevp_alpha3")
@classmethod
def validate_mevp_alpha3(cls, v):
    if not isinstance(v, float) or v <= 0:
        raise ValueError("mevp_alpha3 must be a positive float")
    return v

validate_mevp_alpha4 classmethod

validate_mevp_alpha4(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("mevp_alpha4")
@classmethod
def validate_mevp_alpha4(cls, v):
    if not isinstance(v, float) or v <= 0:
        raise ValueError("mevp_alpha4 must be a positive float")
    return v

validate_pstar classmethod

validate_pstar(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("pstar")
@classmethod
def validate_pstar(cls, v):
    if not isinstance(v, float) or v <= 0:
        raise ValueError("pstar must be a positive float")
    return v

validate_ellipse classmethod

validate_ellipse(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("ellipse")
@classmethod
def validate_ellipse(cls, v):
    if not isinstance(v, float) or v <= 1:
        raise ValueError("ellipse must be a float greater than 1")
    return v

validate_c_pressure classmethod

validate_c_pressure(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("c_pressure")
@classmethod
def validate_c_pressure(cls, v):
    if not isinstance(v, float) or v <= 0:
        raise ValueError("c_pressure must be a positive float")
    return v

validate_ncyc_fct classmethod

validate_ncyc_fct(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("ncyc_fct")
@classmethod
def validate_ncyc_fct(cls, v):
    if not isinstance(v, int) or v <= 0:
        raise ValueError("ncyc_fct must be a positive integer")
    return v

validate_niter_fct classmethod

validate_niter_fct(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("niter_fct")
@classmethod
def validate_niter_fct(cls, v):
    if not isinstance(v, int) or v <= 0:
        raise ValueError("niter_fct must be a positive integer")
    return v

validate_ice_gamma_fct classmethod

validate_ice_gamma_fct(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("ice_gamma_fct")
@classmethod
def validate_ice_gamma_fct(cls, v):
    if not isinstance(v, float) or v < 0 or v > 1:
        raise ValueError("ice_gamma_fct must be a float between 0 and 1")
    return v

validate_depth_ice_fct classmethod

validate_depth_ice_fct(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("depth_ice_fct")
@classmethod
def validate_depth_ice_fct(cls, v):
    if not isinstance(v, float) or v <= 0:
        raise ValueError("depth_ice_fct must be a positive float")
    return v

validate_h_ml0 classmethod

validate_h_ml0(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("h_ml0")
@classmethod
def validate_h_ml0(cls, v):
    if not isinstance(v, float) or v <= 0:
        raise ValueError("h_ml0 must be a positive float")
    return v

validate_salt_ice classmethod

validate_salt_ice(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("salt_ice")
@classmethod
def validate_salt_ice(cls, v):
    if not isinstance(v, float) or v < 0:
        raise ValueError("salt_ice must be a non-negative float")
    return v

validate_salt_water classmethod

validate_salt_water(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("salt_water")
@classmethod
def validate_salt_water(cls, v):
    if not isinstance(v, float) or v < 0:
        raise ValueError("salt_water must be a non-negative float")
    return v

validate_lead_closing classmethod

validate_lead_closing(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("lead_closing")
@classmethod
def validate_lead_closing(cls, v):
    if not isinstance(v, float) or v <= 0:
        raise ValueError("lead_closing must be a positive float")
    return v

validate_saterm classmethod

validate_saterm(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("saterm")
@classmethod
def validate_saterm(cls, v):
    if not isinstance(v, float) or v <= 0 or v > 1:
        raise ValueError("saterm must be a float between 0 and 1")
    return v

validate_albsn classmethod

validate_albsn(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("albsn")
@classmethod
def validate_albsn(cls, v):
    if not isinstance(v, float) or v <= 0 or v > 1:
        raise ValueError("albsn must be a float between 0 and 1")
    return v

validate_albsnm classmethod

validate_albsnm(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("albsnm")
@classmethod
def validate_albsnm(cls, v):
    if not isinstance(v, float) or v <= 0 or v > 1:
        raise ValueError("albsnm must be a float between 0 and 1")
    return v

validate_albi classmethod

validate_albi(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("albi")
@classmethod
def validate_albi(cls, v):
    if not isinstance(v, float) or v <= 0 or v > 1:
        raise ValueError("albi must be a float between 0 and 1")
    return v

validate_albm classmethod

validate_albm(v)
Source code in rompy_schism/namelists/ice.py
@field_validator("albm")
@classmethod
def validate_albm(cls, v):
    if not isinstance(v, float) or v <= 0 or v > 1:
        raise ValueError("albm must be a float between 0 and 1")
    return v

validate_rheol_steps

validate_rheol_steps()
Source code in rompy_schism/namelists/ice.py
@model_validator(mode="after")
def validate_rheol_steps(self):
    if self.ievp == 2 and self.mevp_rheol_steps <= 0:
        raise ValueError("mevp_rheol_steps must be positive when ievp is 2")
    return self

validate_cdwin0_usage

validate_cdwin0_usage()
Source code in rompy_schism/namelists/ice.py
@model_validator(mode="after")
def validate_cdwin0_usage(self):
    if self.ice_atmos_stress_form == 0 and self.cdwin0 <= 0:
        raise ValueError("cdwin0 must be positive when ice_atmos_stress_form is 0")
    return self

validate_mevp_alpha1_usage

validate_mevp_alpha1_usage()
Source code in rompy_schism/namelists/ice.py
@model_validator(mode="after")
def validate_mevp_alpha1_usage(self):
    if self.mevp_coef == 0 and self.mevp_alpha1 <= 0:
        raise ValueError("mevp_alpha1 must be positive when mevp_coef is 0")
    return self

validate_mevp_alpha2_usage

validate_mevp_alpha2_usage()
Source code in rompy_schism/namelists/ice.py
@model_validator(mode="after")
def validate_mevp_alpha2_usage(self):
    if self.mevp_coef == 0 and self.mevp_alpha2 <= 0:
        raise ValueError("mevp_alpha2 must be positive when mevp_coef is 0")
    return self

validate_mevp_alpha3_usage

validate_mevp_alpha3_usage()
Source code in rompy_schism/namelists/ice.py
@model_validator(mode="after")
def validate_mevp_alpha3_usage(self):
    if self.mevp_coef == 1 and self.mevp_alpha3 <= 0:
        raise ValueError("mevp_alpha3 must be positive when mevp_coef is 1")
    return self

validate_mevp_alpha4_usage

validate_mevp_alpha4_usage()
Source code in rompy_schism/namelists/ice.py
@model_validator(mode="after")
def validate_mevp_alpha4_usage(self):
    if self.mevp_coef == 1 and self.mevp_alpha4 <= 0:
        raise ValueError("mevp_alpha4 must be positive when mevp_coef is 1")
    return self

validate_albsnm_relation

validate_albsnm_relation()
Source code in rompy_schism/namelists/ice.py
@model_validator(mode="after")
def validate_albsnm_relation(self):
    if self.albsnm > self.albsn:
        raise ValueError("albsnm must be less than or equal to albsn")
    return self

validate_albi_relation

validate_albi_relation()
Source code in rompy_schism/namelists/ice.py
@model_validator(mode="after")
def validate_albi_relation(self):
    if self.albi > self.albsn:
        raise ValueError("albi must be less than or equal to albsn")
    return self

validate_albm_relation

validate_albm_relation()
Source code in rompy_schism/namelists/ice.py
@model_validator(mode="after")
def validate_albm_relation(self):
    if self.albm > self.albi:
        raise ValueError("albm must be less than or equal to albi")
    return self

Ice

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/ice.py
class Ice(NamelistBaseModel):
    ice_in: Optional[Ice_in] = Field(default_factory=Ice_in)

Attributes

ice_in class-attribute instance-attribute

ice_in: Optional[Ice_in] = Field(default_factory=Ice_in)

MICE

Mice_in

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/mice.py
class Mice_in(NamelistBaseModel):
    ice_tests: Optional[int] = Field(
        0, description="Flag for box test. 0 indicates no box test."
    )
    ihot_mice: Optional[int] = Field(
        1,
        description="Start mode for ice model. 0: cold start, 1: restart, 2: hotstart from HYCOM.",
    )
    ice_advection: Optional[int] = Field(
        6,
        description="Ice advection scheme. 3: upwind, 4: center-difference, 5: tvd, 6: tvd-up, 7: TVD_Casulli.",
    )
    ice_therm_on: Optional[int] = Field(
        1, description="Flag for ice thermodynamics. 1: on, 0: off."
    )
    ievp: Optional[int] = Field(
        2,
        description="Elastic-Viscous-Plastic (EVP) model selection. 1: EVP, 2: modified EVP (mEVP).",
    )
    ice_cutoff: Optional[float] = Field(
        0.001,
        description="Cut-off thickness [m] or fraction for ice. No ice velocity if ice thickness <= ice_cutoff.",
    )
    evp_rheol_steps: Optional[int] = Field(
        500, description="Number of subcycling steps in EVP."
    )
    mevp_rheol_steps: Optional[int] = Field(
        500, description="Number of iterations in modified EVP (mEVP)."
    )
    delta_min: Optional[float] = Field(
        1e-11,
        description="Limit for minimum divergence (1/s). Used in both VP and EVP.",
    )
    theta_io: Optional[float] = Field(
        0.0, description="Ice/ocean rotation angle in degrees."
    )
    mevp_alpha1: Optional[float] = Field(
        200.0, description="Constant used in mEVP for constitutive equation."
    )
    mevp_alpha2: Optional[float] = Field(
        200.0, description="Constant used in mEVP for momentum equation."
    )
    pstar: Optional[float] = Field(
        27500.0, description="Ice strength parameter [N/m^2]."
    )
    ellipse: Optional[float] = Field(
        2.0, description="Ellipticity parameter for ice rheology."
    )
    c_pressure: Optional[float] = Field(
        20.0, description="Ice pressure coefficient [-]."
    )
    niter_fct: Optional[int] = Field(
        3,
        description="Number of iterations in higher-order solve for FCT (Flux-Corrected Transport).",
    )
    ice_gamma_fct: Optional[float] = Field(
        0.25, description="Smoothing parameter for FCT."
    )
    h_ml0: Optional[float] = Field(
        0.1, description="Initial ocean mixed layer depth [m]."
    )
    salt_ice: Optional[float] = Field(5.0, description="Salinity for ice [PSU].")
    salt_water: Optional[float] = Field(34.0, description="Salinity for water [PSU].")

    @field_validator("ice_tests")
    @classmethod
    def validate_ice_tests(cls, v):
        if not isinstance(v, int) or v not in [0, 1]:
            raise ValueError("ice_tests must be 0 or 1")
        return v

    @field_validator("ihot_mice")
    @classmethod
    def validate_ihot_mice(cls, v):
        if not isinstance(v, int) or v not in [0, 1, 2]:
            raise ValueError("ihot_mice must be 0, 1, or 2")
        return v

    @field_validator("ice_advection")
    @classmethod
    def validate_ice_advection(cls, v):
        if not isinstance(v, int) or v not in range(3, 8):
            raise ValueError("ice_advection must be an integer between 3 and 7")
        return v

    @field_validator("ice_therm_on")
    @classmethod
    def validate_ice_therm_on(cls, v):
        if not isinstance(v, int) or v not in [0, 1]:
            raise ValueError("ice_therm_on must be 0 or 1")
        return v

    @field_validator("ievp")
    @classmethod
    def validate_ievp(cls, v):
        if not isinstance(v, int) or v not in [1, 2]:
            raise ValueError("ievp must be 1 or 2")
        return v

    @field_validator("ice_cutoff")
    @classmethod
    def validate_ice_cutoff(cls, v):
        if not isinstance(v, float) or v < 0:
            raise ValueError("ice_cutoff must be a non-negative float")
        return v

    @field_validator("evp_rheol_steps")
    @classmethod
    def validate_evp_rheol_steps(cls, v):
        if not isinstance(v, int) or v <= 0:
            raise ValueError("evp_rheol_steps must be a positive integer")
        return v

    @field_validator("mevp_rheol_steps")
    @classmethod
    def validate_mevp_rheol_steps(cls, v):
        if not isinstance(v, int) or v <= 0:
            raise ValueError("mevp_rheol_steps must be a positive integer")
        return v

    @field_validator("delta_min")
    @classmethod
    def validate_delta_min(cls, v):
        if not isinstance(v, float) or v <= 0:
            raise ValueError("delta_min must be a positive float")
        return v

    @field_validator("theta_io")
    @classmethod
    def validate_theta_io(cls, v):
        if not isinstance(v, float) or v < 0 or v >= 360:
            raise ValueError("theta_io must be a float between 0 and 360")
        return v

    @field_validator("mevp_alpha1")
    @classmethod
    def validate_mevp_alpha1(cls, v):
        if not isinstance(v, float) or v <= 0:
            raise ValueError("mevp_alpha1 must be a positive float")
        return v

    @field_validator("mevp_alpha2")
    @classmethod
    def validate_mevp_alpha2(cls, v):
        if not isinstance(v, float) or v <= 0:
            raise ValueError("mevp_alpha2 must be a positive float")
        return v

    @field_validator("pstar")
    @classmethod
    def validate_pstar(cls, v):
        if not isinstance(v, float) or v <= 0:
            raise ValueError("pstar must be a positive float")
        return v

    @field_validator("ellipse")
    @classmethod
    def validate_ellipse(cls, v):
        if not isinstance(v, float) or v <= 1:
            raise ValueError("ellipse must be a float greater than 1")
        return v

    @field_validator("c_pressure")
    @classmethod
    def validate_c_pressure(cls, v):
        if not isinstance(v, float) or v <= 0:
            raise ValueError("c_pressure must be a positive float")
        return v

    @field_validator("niter_fct")
    @classmethod
    def validate_niter_fct(cls, v):
        if not isinstance(v, int) or v <= 0:
            raise ValueError("niter_fct must be a positive integer")
        return v

    @field_validator("ice_gamma_fct")
    @classmethod
    def validate_ice_gamma_fct(cls, v):
        if not isinstance(v, float) or v < 0 or v > 1:
            raise ValueError("ice_gamma_fct must be a float between 0 and 1")
        return v

    @field_validator("h_ml0")
    @classmethod
    def validate_h_ml0(cls, v):
        if not isinstance(v, float) or v <= 0:
            raise ValueError("h_ml0 must be a positive float")
        return v

    @field_validator("salt_ice")
    @classmethod
    def validate_salt_ice(cls, v):
        if not isinstance(v, float) or v < 0:
            raise ValueError("salt_ice must be a non-negative float")
        return v

    @field_validator("salt_water")
    @classmethod
    def validate_salt_water(cls, v):
        if not isinstance(v, float) or v < 0:
            raise ValueError("salt_water must be a non-negative float")
        return v

Attributes

ice_tests class-attribute instance-attribute

ice_tests: Optional[int] = Field(0, description='Flag for box test. 0 indicates no box test.')

ihot_mice class-attribute instance-attribute

ihot_mice: Optional[int] = Field(1, description='Start mode for ice model. 0: cold start, 1: restart, 2: hotstart from HYCOM.')

ice_advection class-attribute instance-attribute

ice_advection: Optional[int] = Field(6, description='Ice advection scheme. 3: upwind, 4: center-difference, 5: tvd, 6: tvd-up, 7: TVD_Casulli.')

ice_therm_on class-attribute instance-attribute

ice_therm_on: Optional[int] = Field(1, description='Flag for ice thermodynamics. 1: on, 0: off.')

ievp class-attribute instance-attribute

ievp: Optional[int] = Field(2, description='Elastic-Viscous-Plastic (EVP) model selection. 1: EVP, 2: modified EVP (mEVP).')

ice_cutoff class-attribute instance-attribute

ice_cutoff: Optional[float] = Field(0.001, description='Cut-off thickness [m] or fraction for ice. No ice velocity if ice thickness <= ice_cutoff.')

evp_rheol_steps class-attribute instance-attribute

evp_rheol_steps: Optional[int] = Field(500, description='Number of subcycling steps in EVP.')

mevp_rheol_steps class-attribute instance-attribute

mevp_rheol_steps: Optional[int] = Field(500, description='Number of iterations in modified EVP (mEVP).')

delta_min class-attribute instance-attribute

delta_min: Optional[float] = Field(1e-11, description='Limit for minimum divergence (1/s). Used in both VP and EVP.')

theta_io class-attribute instance-attribute

theta_io: Optional[float] = Field(0.0, description='Ice/ocean rotation angle in degrees.')

mevp_alpha1 class-attribute instance-attribute

mevp_alpha1: Optional[float] = Field(200.0, description='Constant used in mEVP for constitutive equation.')

mevp_alpha2 class-attribute instance-attribute

mevp_alpha2: Optional[float] = Field(200.0, description='Constant used in mEVP for momentum equation.')

pstar class-attribute instance-attribute

pstar: Optional[float] = Field(27500.0, description='Ice strength parameter [N/m^2].')

ellipse class-attribute instance-attribute

ellipse: Optional[float] = Field(2.0, description='Ellipticity parameter for ice rheology.')

c_pressure class-attribute instance-attribute

c_pressure: Optional[float] = Field(20.0, description='Ice pressure coefficient [-].')

niter_fct class-attribute instance-attribute

niter_fct: Optional[int] = Field(3, description='Number of iterations in higher-order solve for FCT (Flux-Corrected Transport).')

ice_gamma_fct class-attribute instance-attribute

ice_gamma_fct: Optional[float] = Field(0.25, description='Smoothing parameter for FCT.')

h_ml0 class-attribute instance-attribute

h_ml0: Optional[float] = Field(0.1, description='Initial ocean mixed layer depth [m].')

salt_ice class-attribute instance-attribute

salt_ice: Optional[float] = Field(5.0, description='Salinity for ice [PSU].')

salt_water class-attribute instance-attribute

salt_water: Optional[float] = Field(34.0, description='Salinity for water [PSU].')

Functions

validate_ice_tests classmethod

validate_ice_tests(v)
Source code in rompy_schism/namelists/mice.py
@field_validator("ice_tests")
@classmethod
def validate_ice_tests(cls, v):
    if not isinstance(v, int) or v not in [0, 1]:
        raise ValueError("ice_tests must be 0 or 1")
    return v

validate_ihot_mice classmethod

validate_ihot_mice(v)
Source code in rompy_schism/namelists/mice.py
@field_validator("ihot_mice")
@classmethod
def validate_ihot_mice(cls, v):
    if not isinstance(v, int) or v not in [0, 1, 2]:
        raise ValueError("ihot_mice must be 0, 1, or 2")
    return v

validate_ice_advection classmethod

validate_ice_advection(v)
Source code in rompy_schism/namelists/mice.py
@field_validator("ice_advection")
@classmethod
def validate_ice_advection(cls, v):
    if not isinstance(v, int) or v not in range(3, 8):
        raise ValueError("ice_advection must be an integer between 3 and 7")
    return v

validate_ice_therm_on classmethod

validate_ice_therm_on(v)
Source code in rompy_schism/namelists/mice.py
@field_validator("ice_therm_on")
@classmethod
def validate_ice_therm_on(cls, v):
    if not isinstance(v, int) or v not in [0, 1]:
        raise ValueError("ice_therm_on must be 0 or 1")
    return v

validate_ievp classmethod

validate_ievp(v)
Source code in rompy_schism/namelists/mice.py
@field_validator("ievp")
@classmethod
def validate_ievp(cls, v):
    if not isinstance(v, int) or v not in [1, 2]:
        raise ValueError("ievp must be 1 or 2")
    return v

validate_ice_cutoff classmethod

validate_ice_cutoff(v)
Source code in rompy_schism/namelists/mice.py
@field_validator("ice_cutoff")
@classmethod
def validate_ice_cutoff(cls, v):
    if not isinstance(v, float) or v < 0:
        raise ValueError("ice_cutoff must be a non-negative float")
    return v

validate_evp_rheol_steps classmethod

validate_evp_rheol_steps(v)
Source code in rompy_schism/namelists/mice.py
@field_validator("evp_rheol_steps")
@classmethod
def validate_evp_rheol_steps(cls, v):
    if not isinstance(v, int) or v <= 0:
        raise ValueError("evp_rheol_steps must be a positive integer")
    return v

validate_mevp_rheol_steps classmethod

validate_mevp_rheol_steps(v)
Source code in rompy_schism/namelists/mice.py
@field_validator("mevp_rheol_steps")
@classmethod
def validate_mevp_rheol_steps(cls, v):
    if not isinstance(v, int) or v <= 0:
        raise ValueError("mevp_rheol_steps must be a positive integer")
    return v

validate_delta_min classmethod

validate_delta_min(v)
Source code in rompy_schism/namelists/mice.py
@field_validator("delta_min")
@classmethod
def validate_delta_min(cls, v):
    if not isinstance(v, float) or v <= 0:
        raise ValueError("delta_min must be a positive float")
    return v

validate_theta_io classmethod

validate_theta_io(v)
Source code in rompy_schism/namelists/mice.py
@field_validator("theta_io")
@classmethod
def validate_theta_io(cls, v):
    if not isinstance(v, float) or v < 0 or v >= 360:
        raise ValueError("theta_io must be a float between 0 and 360")
    return v

validate_mevp_alpha1 classmethod

validate_mevp_alpha1(v)
Source code in rompy_schism/namelists/mice.py
@field_validator("mevp_alpha1")
@classmethod
def validate_mevp_alpha1(cls, v):
    if not isinstance(v, float) or v <= 0:
        raise ValueError("mevp_alpha1 must be a positive float")
    return v

validate_mevp_alpha2 classmethod

validate_mevp_alpha2(v)
Source code in rompy_schism/namelists/mice.py
@field_validator("mevp_alpha2")
@classmethod
def validate_mevp_alpha2(cls, v):
    if not isinstance(v, float) or v <= 0:
        raise ValueError("mevp_alpha2 must be a positive float")
    return v

validate_pstar classmethod

validate_pstar(v)
Source code in rompy_schism/namelists/mice.py
@field_validator("pstar")
@classmethod
def validate_pstar(cls, v):
    if not isinstance(v, float) or v <= 0:
        raise ValueError("pstar must be a positive float")
    return v

validate_ellipse classmethod

validate_ellipse(v)
Source code in rompy_schism/namelists/mice.py
@field_validator("ellipse")
@classmethod
def validate_ellipse(cls, v):
    if not isinstance(v, float) or v <= 1:
        raise ValueError("ellipse must be a float greater than 1")
    return v

validate_c_pressure classmethod

validate_c_pressure(v)
Source code in rompy_schism/namelists/mice.py
@field_validator("c_pressure")
@classmethod
def validate_c_pressure(cls, v):
    if not isinstance(v, float) or v <= 0:
        raise ValueError("c_pressure must be a positive float")
    return v

validate_niter_fct classmethod

validate_niter_fct(v)
Source code in rompy_schism/namelists/mice.py
@field_validator("niter_fct")
@classmethod
def validate_niter_fct(cls, v):
    if not isinstance(v, int) or v <= 0:
        raise ValueError("niter_fct must be a positive integer")
    return v

validate_ice_gamma_fct classmethod

validate_ice_gamma_fct(v)
Source code in rompy_schism/namelists/mice.py
@field_validator("ice_gamma_fct")
@classmethod
def validate_ice_gamma_fct(cls, v):
    if not isinstance(v, float) or v < 0 or v > 1:
        raise ValueError("ice_gamma_fct must be a float between 0 and 1")
    return v

validate_h_ml0 classmethod

validate_h_ml0(v)
Source code in rompy_schism/namelists/mice.py
@field_validator("h_ml0")
@classmethod
def validate_h_ml0(cls, v):
    if not isinstance(v, float) or v <= 0:
        raise ValueError("h_ml0 must be a positive float")
    return v

validate_salt_ice classmethod

validate_salt_ice(v)
Source code in rompy_schism/namelists/mice.py
@field_validator("salt_ice")
@classmethod
def validate_salt_ice(cls, v):
    if not isinstance(v, float) or v < 0:
        raise ValueError("salt_ice must be a non-negative float")
    return v

validate_salt_water classmethod

validate_salt_water(v)
Source code in rompy_schism/namelists/mice.py
@field_validator("salt_water")
@classmethod
def validate_salt_water(cls, v):
    if not isinstance(v, float) or v < 0:
        raise ValueError("salt_water must be a non-negative float")
    return v

Mice

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/mice.py
class Mice(NamelistBaseModel):
    mice_in: Optional[Mice_in] = Field(default_factory=Mice_in)

Attributes

mice_in class-attribute instance-attribute

mice_in: Optional[Mice_in] = Field(default_factory=Mice_in)

ICM

Bag

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/icm.py
class Bag(NamelistBaseModel):
    gpatch0: Optional[int] = Field(
        -999,
        description="Region flag for Benthic Algae (BA). 1 indicates BA is ON for all elements, -999 indicates spatial distribution.",
    )
    ba0: Optional[float] = Field(
        5.0,
        description="Initial Benthic Algae (BA) concentration in grams of carbon per square meter.",
    )
    ggpm: Optional[float] = Field(
        2.25, description="Maximum growth rate for Benthic Algae (BA) per day."
    )
    gtgp: Optional[float] = Field(
        20.0,
        description="Optimal temperature for Benthic Algae (BA) growth in degrees Celsius.",
    )
    gktgp: Optional[list] = Field(
        [0.004, 0.006],
        description="Temperature dependence coefficients for Benthic Algae (BA) growth in inverse degrees Celsius squared.",
    )
    gmtb: Optional[float] = Field(
        0.05,
        description="Respiration rate for Benthic Algae (BA) at the reference temperature gTR, per day.",
    )
    gprr: Optional[float] = Field(
        0.1,
        description="Predation rate for Benthic Algae (BA) at the reference temperature gTR, per day.",
    )
    gtr: Optional[float] = Field(
        20.0,
        description="Reference temperature for Benthic Algae (BA) respiration in degrees Celsius.",
    )
    gktr: Optional[float] = Field(
        0.069,
        description="Temperature dependence coefficient for Benthic Algae (BA) respiration in inverse degrees Celsius.",
    )
    galpha: Optional[float] = Field(
        0.1,
        description="Initial slope of the Photosynthesis-Irradiance (P-I) curve for Benthic Algae (BA) in square meters per Einstein.",
    )
    gksed: Optional[float] = Field(
        0.0,
        description="Light attenuation due to sediment for Benthic Algae (BA) growth (dimensionless).",
    )
    gkba: Optional[float] = Field(
        0.01,
        description="Light attenuation coefficient due to Benthic Algae (BA) self-shading in square meters per gram of carbon.",
    )
    gkhn: Optional[float] = Field(
        0.01,
        description="Nitrogen half-saturation constant for Benthic Algae (BA) growth in grams of nitrogen per square meter.",
    )
    gkhp: Optional[float] = Field(
        0.001,
        description="Phosphorus half-saturation constant for Benthic Algae (BA) growth in grams of phosphorus per square meter.",
    )
    gp2c: Optional[float] = Field(
        0.0167, description="Phosphorus to carbon ratio in Benthic Algae (BA) biomass."
    )
    gn2c: Optional[float] = Field(
        0.167, description="Nitrogen to carbon ratio in Benthic Algae (BA) biomass."
    )
    go2c: Optional[float] = Field(
        2.67, description="Oxygen to carbon ratio for Benthic Algae (BA) respiration."
    )
    gfcp: Optional[list] = Field(
        [0.5, 0.45, 0.05],
        description="Fractions of predated Benthic Algae (BA) carbon distributed into three classes in sediment.",
    )
    gfnp: Optional[list] = Field(
        [0.5, 0.45, 0.05],
        description="Fractions of predated Benthic Algae (BA) nitrogen distributed into three classes in sediment.",
    )
    gfpp: Optional[list] = Field(
        [0.5, 0.45, 0.05],
        description="Fractions of predated Benthic Algae (BA) phosphorus distributed into three classes in sediment.",
    )

    @field_validator("gpatch0")
    @classmethod
    def check_gpatch0(cls, v):
        if v not in [1, -999]:
            raise ValueError("gpatch0 must be either 1 or -999")
        return v

    @field_validator("ba0")
    @classmethod
    def check_ba0(cls, v):
        if v <= 0:
            raise ValueError("BA0 must be positive")
        return v

    @field_validator("ggpm")
    @classmethod
    def check_ggpm(cls, v):
        if v <= 0:
            raise ValueError("gGPM must be positive")
        return v

    @field_validator("gtgp")
    @classmethod
    def check_gtgp(cls, v):
        if v < 0 or v > 100:
            raise ValueError("gTGP must be between 0 and 100")
        return v

    @field_validator("gktgp")
    @classmethod
    def check_gktgp(cls, v):
        if len(v) != 2 or any(x <= 0 for x in v):
            raise ValueError("gKTGP must be a list of two positive values")
        return v

    @field_validator("gmtb")
    @classmethod
    def check_gmtb(cls, v):
        if v < 0 or v > 1:
            raise ValueError("gMTB must be between 0 and 1")
        return v

    @field_validator("gprr")
    @classmethod
    def check_gprr(cls, v):
        if v < 0 or v > 1:
            raise ValueError("gPRR must be between 0 and 1")
        return v

    @field_validator("gtr")
    @classmethod
    def check_gtr(cls, v):
        if v < 0 or v > 100:
            raise ValueError("gTR must be between 0 and 100")
        return v

    @field_validator("gktr")
    @classmethod
    def check_gktr(cls, v):
        if v <= 0:
            raise ValueError("gKTR must be positive")
        return v

    @field_validator("galpha")
    @classmethod
    def check_galpha(cls, v):
        if v <= 0:
            raise ValueError("galpha must be positive")
        return v

    @field_validator("gksed")
    @classmethod
    def check_gksed(cls, v):
        if v < 0:
            raise ValueError("gKSED must be non-negative")
        return v

    @field_validator("gkba")
    @classmethod
    def check_gkba(cls, v):
        if v <= 0:
            raise ValueError("gKBA must be positive")
        return v

    @field_validator("gkhn")
    @classmethod
    def check_gkhn(cls, v):
        if v <= 0:
            raise ValueError("gKhN must be positive")
        return v

    @field_validator("gkhp")
    @classmethod
    def check_gkhp(cls, v):
        if v <= 0:
            raise ValueError("gKhP must be positive")
        return v

    @field_validator("gp2c")
    @classmethod
    def check_gp2c(cls, v):
        if v <= 0 or v >= 1:
            raise ValueError("gp2c must be between 0 and 1")
        return v

    @field_validator("gn2c")
    @classmethod
    def check_gn2c(cls, v):
        if v <= 0 or v >= 1:
            raise ValueError("gn2c must be between 0 and 1")
        return v

    @field_validator("go2c")
    @classmethod
    def check_go2c(cls, v):
        if v <= 0:
            raise ValueError("go2c must be positive")
        return v

    @field_validator("gfcp")
    @classmethod
    def check_gfcp(cls, v):
        if len(v) != 3 or any(x < 0 or x > 1 for x in v) or sum(v) != 1:
            raise ValueError(
                "gFCP must be a list of 3 values between 0 and 1, summing to 1"
            )
        return v

    @field_validator("gfnp")
    @classmethod
    def check_gfnp(cls, v):
        if len(v) != 3 or any(x < 0 or x > 1 for x in v) or sum(v) != 1:
            raise ValueError(
                "gFNP must be a list of 3 values between 0 and 1, summing to 1"
            )
        return v

    @field_validator("gfpp")
    @classmethod
    def check_gfpp(cls, v):
        if len(v) != 3 or any(x < 0 or x > 1 for x in v) or sum(v) != 1:
            raise ValueError(
                "gFPP must be a list of 3 values between 0 and 1, summing to 1"
            )
        return v

Attributes

gpatch0 class-attribute instance-attribute

gpatch0: Optional[int] = Field(-999, description='Region flag for Benthic Algae (BA). 1 indicates BA is ON for all elements, -999 indicates spatial distribution.')

ba0 class-attribute instance-attribute

ba0: Optional[float] = Field(5.0, description='Initial Benthic Algae (BA) concentration in grams of carbon per square meter.')

ggpm class-attribute instance-attribute

ggpm: Optional[float] = Field(2.25, description='Maximum growth rate for Benthic Algae (BA) per day.')

gtgp class-attribute instance-attribute

gtgp: Optional[float] = Field(20.0, description='Optimal temperature for Benthic Algae (BA) growth in degrees Celsius.')

gktgp class-attribute instance-attribute

gktgp: Optional[list] = Field([0.004, 0.006], description='Temperature dependence coefficients for Benthic Algae (BA) growth in inverse degrees Celsius squared.')

gmtb class-attribute instance-attribute

gmtb: Optional[float] = Field(0.05, description='Respiration rate for Benthic Algae (BA) at the reference temperature gTR, per day.')

gprr class-attribute instance-attribute

gprr: Optional[float] = Field(0.1, description='Predation rate for Benthic Algae (BA) at the reference temperature gTR, per day.')

gtr class-attribute instance-attribute

gtr: Optional[float] = Field(20.0, description='Reference temperature for Benthic Algae (BA) respiration in degrees Celsius.')

gktr class-attribute instance-attribute

gktr: Optional[float] = Field(0.069, description='Temperature dependence coefficient for Benthic Algae (BA) respiration in inverse degrees Celsius.')

galpha class-attribute instance-attribute

galpha: Optional[float] = Field(0.1, description='Initial slope of the Photosynthesis-Irradiance (P-I) curve for Benthic Algae (BA) in square meters per Einstein.')

gksed class-attribute instance-attribute

gksed: Optional[float] = Field(0.0, description='Light attenuation due to sediment for Benthic Algae (BA) growth (dimensionless).')

gkba class-attribute instance-attribute

gkba: Optional[float] = Field(0.01, description='Light attenuation coefficient due to Benthic Algae (BA) self-shading in square meters per gram of carbon.')

gkhn class-attribute instance-attribute

gkhn: Optional[float] = Field(0.01, description='Nitrogen half-saturation constant for Benthic Algae (BA) growth in grams of nitrogen per square meter.')

gkhp class-attribute instance-attribute

gkhp: Optional[float] = Field(0.001, description='Phosphorus half-saturation constant for Benthic Algae (BA) growth in grams of phosphorus per square meter.')

gp2c class-attribute instance-attribute

gp2c: Optional[float] = Field(0.0167, description='Phosphorus to carbon ratio in Benthic Algae (BA) biomass.')

gn2c class-attribute instance-attribute

gn2c: Optional[float] = Field(0.167, description='Nitrogen to carbon ratio in Benthic Algae (BA) biomass.')

go2c class-attribute instance-attribute

go2c: Optional[float] = Field(2.67, description='Oxygen to carbon ratio for Benthic Algae (BA) respiration.')

gfcp class-attribute instance-attribute

gfcp: Optional[list] = Field([0.5, 0.45, 0.05], description='Fractions of predated Benthic Algae (BA) carbon distributed into three classes in sediment.')

gfnp class-attribute instance-attribute

gfnp: Optional[list] = Field([0.5, 0.45, 0.05], description='Fractions of predated Benthic Algae (BA) nitrogen distributed into three classes in sediment.')

gfpp class-attribute instance-attribute

gfpp: Optional[list] = Field([0.5, 0.45, 0.05], description='Fractions of predated Benthic Algae (BA) phosphorus distributed into three classes in sediment.')

Functions

check_gpatch0 classmethod

check_gpatch0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("gpatch0")
@classmethod
def check_gpatch0(cls, v):
    if v not in [1, -999]:
        raise ValueError("gpatch0 must be either 1 or -999")
    return v

check_ba0 classmethod

check_ba0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ba0")
@classmethod
def check_ba0(cls, v):
    if v <= 0:
        raise ValueError("BA0 must be positive")
    return v

check_ggpm classmethod

check_ggpm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ggpm")
@classmethod
def check_ggpm(cls, v):
    if v <= 0:
        raise ValueError("gGPM must be positive")
    return v

check_gtgp classmethod

check_gtgp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("gtgp")
@classmethod
def check_gtgp(cls, v):
    if v < 0 or v > 100:
        raise ValueError("gTGP must be between 0 and 100")
    return v

check_gktgp classmethod

check_gktgp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("gktgp")
@classmethod
def check_gktgp(cls, v):
    if len(v) != 2 or any(x <= 0 for x in v):
        raise ValueError("gKTGP must be a list of two positive values")
    return v

check_gmtb classmethod

check_gmtb(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("gmtb")
@classmethod
def check_gmtb(cls, v):
    if v < 0 or v > 1:
        raise ValueError("gMTB must be between 0 and 1")
    return v

check_gprr classmethod

check_gprr(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("gprr")
@classmethod
def check_gprr(cls, v):
    if v < 0 or v > 1:
        raise ValueError("gPRR must be between 0 and 1")
    return v

check_gtr classmethod

check_gtr(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("gtr")
@classmethod
def check_gtr(cls, v):
    if v < 0 or v > 100:
        raise ValueError("gTR must be between 0 and 100")
    return v

check_gktr classmethod

check_gktr(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("gktr")
@classmethod
def check_gktr(cls, v):
    if v <= 0:
        raise ValueError("gKTR must be positive")
    return v

check_galpha classmethod

check_galpha(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("galpha")
@classmethod
def check_galpha(cls, v):
    if v <= 0:
        raise ValueError("galpha must be positive")
    return v

check_gksed classmethod

check_gksed(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("gksed")
@classmethod
def check_gksed(cls, v):
    if v < 0:
        raise ValueError("gKSED must be non-negative")
    return v

check_gkba classmethod

check_gkba(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("gkba")
@classmethod
def check_gkba(cls, v):
    if v <= 0:
        raise ValueError("gKBA must be positive")
    return v

check_gkhn classmethod

check_gkhn(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("gkhn")
@classmethod
def check_gkhn(cls, v):
    if v <= 0:
        raise ValueError("gKhN must be positive")
    return v

check_gkhp classmethod

check_gkhp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("gkhp")
@classmethod
def check_gkhp(cls, v):
    if v <= 0:
        raise ValueError("gKhP must be positive")
    return v

check_gp2c classmethod

check_gp2c(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("gp2c")
@classmethod
def check_gp2c(cls, v):
    if v <= 0 or v >= 1:
        raise ValueError("gp2c must be between 0 and 1")
    return v

check_gn2c classmethod

check_gn2c(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("gn2c")
@classmethod
def check_gn2c(cls, v):
    if v <= 0 or v >= 1:
        raise ValueError("gn2c must be between 0 and 1")
    return v

check_go2c classmethod

check_go2c(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("go2c")
@classmethod
def check_go2c(cls, v):
    if v <= 0:
        raise ValueError("go2c must be positive")
    return v

check_gfcp classmethod

check_gfcp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("gfcp")
@classmethod
def check_gfcp(cls, v):
    if len(v) != 3 or any(x < 0 or x > 1 for x in v) or sum(v) != 1:
        raise ValueError(
            "gFCP must be a list of 3 values between 0 and 1, summing to 1"
        )
    return v

check_gfnp classmethod

check_gfnp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("gfnp")
@classmethod
def check_gfnp(cls, v):
    if len(v) != 3 or any(x < 0 or x > 1 for x in v) or sum(v) != 1:
        raise ValueError(
            "gFNP must be a list of 3 values between 0 and 1, summing to 1"
        )
    return v

check_gfpp classmethod

check_gfpp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("gfpp")
@classmethod
def check_gfpp(cls, v):
    if len(v) != 3 or any(x < 0 or x > 1 for x in v) or sum(v) != 1:
        raise ValueError(
            "gFPP must be a list of 3 values between 0 and 1, summing to 1"
        )
    return v

Core

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/icm.py
 358
 359
 360
 361
 362
 363
 364
 365
 366
 367
 368
 369
 370
 371
 372
 373
 374
 375
 376
 377
 378
 379
 380
 381
 382
 383
 384
 385
 386
 387
 388
 389
 390
 391
 392
 393
 394
 395
 396
 397
 398
 399
 400
 401
 402
 403
 404
 405
 406
 407
 408
 409
 410
 411
 412
 413
 414
 415
 416
 417
 418
 419
 420
 421
 422
 423
 424
 425
 426
 427
 428
 429
 430
 431
 432
 433
 434
 435
 436
 437
 438
 439
 440
 441
 442
 443
 444
 445
 446
 447
 448
 449
 450
 451
 452
 453
 454
 455
 456
 457
 458
 459
 460
 461
 462
 463
 464
 465
 466
 467
 468
 469
 470
 471
 472
 473
 474
 475
 476
 477
 478
 479
 480
 481
 482
 483
 484
 485
 486
 487
 488
 489
 490
 491
 492
 493
 494
 495
 496
 497
 498
 499
 500
 501
 502
 503
 504
 505
 506
 507
 508
 509
 510
 511
 512
 513
 514
 515
 516
 517
 518
 519
 520
 521
 522
 523
 524
 525
 526
 527
 528
 529
 530
 531
 532
 533
 534
 535
 536
 537
 538
 539
 540
 541
 542
 543
 544
 545
 546
 547
 548
 549
 550
 551
 552
 553
 554
 555
 556
 557
 558
 559
 560
 561
 562
 563
 564
 565
 566
 567
 568
 569
 570
 571
 572
 573
 574
 575
 576
 577
 578
 579
 580
 581
 582
 583
 584
 585
 586
 587
 588
 589
 590
 591
 592
 593
 594
 595
 596
 597
 598
 599
 600
 601
 602
 603
 604
 605
 606
 607
 608
 609
 610
 611
 612
 613
 614
 615
 616
 617
 618
 619
 620
 621
 622
 623
 624
 625
 626
 627
 628
 629
 630
 631
 632
 633
 634
 635
 636
 637
 638
 639
 640
 641
 642
 643
 644
 645
 646
 647
 648
 649
 650
 651
 652
 653
 654
 655
 656
 657
 658
 659
 660
 661
 662
 663
 664
 665
 666
 667
 668
 669
 670
 671
 672
 673
 674
 675
 676
 677
 678
 679
 680
 681
 682
 683
 684
 685
 686
 687
 688
 689
 690
 691
 692
 693
 694
 695
 696
 697
 698
 699
 700
 701
 702
 703
 704
 705
 706
 707
 708
 709
 710
 711
 712
 713
 714
 715
 716
 717
 718
 719
 720
 721
 722
 723
 724
 725
 726
 727
 728
 729
 730
 731
 732
 733
 734
 735
 736
 737
 738
 739
 740
 741
 742
 743
 744
 745
 746
 747
 748
 749
 750
 751
 752
 753
 754
 755
 756
 757
 758
 759
 760
 761
 762
 763
 764
 765
 766
 767
 768
 769
 770
 771
 772
 773
 774
 775
 776
 777
 778
 779
 780
 781
 782
 783
 784
 785
 786
 787
 788
 789
 790
 791
 792
 793
 794
 795
 796
 797
 798
 799
 800
 801
 802
 803
 804
 805
 806
 807
 808
 809
 810
 811
 812
 813
 814
 815
 816
 817
 818
 819
 820
 821
 822
 823
 824
 825
 826
 827
 828
 829
 830
 831
 832
 833
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
class Core(NamelistBaseModel):
    gpm: Optional[list] = Field(
        [2.5, 2.8, 3.5],
        description="Phytoplankton growth rates for three different species (day^-1)",
    )
    tgp: Optional[list] = Field(
        [15.0, 22.0, 27.0],
        description="Optimal temperatures for phytoplankton growth for three different species (°C)",
    )
    ktgp: Optional[list] = Field(
        [0.005, 0.004, 0.003, 0.008, 0.006, 0.004],
        description="Temperature dependence for phytoplankton growth, dimensioned as (PB=1:3,1:2) (°C^-2)",
    )
    mtr: Optional[list] = Field(
        [0.0, 0.0, 0.0],
        description="Phytoplankton photorespiration coefficients for three species (0 < MTR < 1)",
    )
    mtb: Optional[list] = Field(
        [0.01, 0.02, 0.03],
        description="Phytoplankton metabolism rates for three species (day^-1)",
    )
    tmt: Optional[list] = Field(
        [20.0, 20.0, 20.0],
        description="Reference temperatures for phytoplankton metabolism for three species (°C)",
    )
    ktmt: Optional[list] = Field(
        [0.0322, 0.0322, 0.0322],
        description="Temperature dependence for phytoplankton metabolism for three species (°C^-1)",
    )
    fcp: Optional[list] = Field(
        [0.35, 0.3, 0.2, 0.55, 0.5, 0.5, 0.1, 0.2, 0.3, 0.0, 0.0, 0.0],
        description="Fractions of phytoplankton carbon into (RPOC,LPOC,DOC,SRPOC) for three species",
    )
    fnp: Optional[list] = Field(
        [
            0.35,
            0.35,
            0.35,
            0.5,
            0.5,
            0.5,
            0.1,
            0.1,
            0.1,
            0.05,
            0.05,
            0.05,
            0.0,
            0.0,
            0.0,
        ],
        description="Fractions of phytoplankton nitrogen into (RPON,LPON,DON,NH4,SRPON) for three species",
    )
    fpp: Optional[list] = Field(
        [0.1, 0.1, 0.1, 0.2, 0.2, 0.2, 0.5, 0.5, 0.5, 0.2, 0.2, 0.2, 0.0, 0.0, 0.0],
        description="Fractions of phytoplankton Phosphorus into (RPOP,LPOP,DOP,PO4,SRPOP) for three species",
    )
    fcm: Optional[list] = Field(
        [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.1, 0.1, 0.0, 0.0, 0.0],
        description="Fractions of phytoplankton metabolism carbon into (RPOC,LPOC,DOC,SRPOC) for three species",
    )
    fnm: Optional[list] = Field(
        [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
        description="Fractions of phytoplankton metabolism nitrogen into (RPON,LPON,DON,NH4,SRPON) for three species",
    )
    fpm: Optional[list] = Field(
        [0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0],
        description="Fractions of phytoplankton metabolism phosphorus into (RPOP,LPOP,DOP,PO4,SRPOP) for three species",
    )
    nit: Optional[float] = Field(
        0.07, description="Maximum nitrification rate (day^-1)"
    )
    tnit: Optional[float] = Field(
        27.0, description="Optimal temperature for nitrification (°C)"
    )
    ktnit: Optional[list] = Field(
        [0.0045, 0.0045],
        description="Temperature dependence for nitrification (T<=TNit & T>TNit) (°C^-2)",
    )
    khdon: Optional[float] = Field(
        1.0, description="Dissolved Oxygen half saturation for nitrification (mg/L)"
    )
    khdoox: Optional[float] = Field(
        0.5,
        description="Dissolved Oxygen half saturation for denitrification & DOC's oxic respiration (mg/L)",
    )
    khno3dn: Optional[float] = Field(
        0.1, description="Nitrate half saturation for denitrification (mg/L)"
    )
    kc0: Optional[list] = Field(
        [0.005, 0.075, 0.2],
        description="Minimum decay rates of RPOC, LPOC, DOC (day^-1)",
    )
    kn0: Optional[list] = Field(
        [0.005, 0.075, 0.2],
        description="Minimum decay rates of RPON, LPON, DON (day^-1)",
    )
    kp0: Optional[list] = Field(
        [0.005, 0.075, 0.2],
        description="Minimum decay rates of RPOP, LPOP, DOP (day^-1)",
    )
    kcalg: Optional[list] = Field(
        [0.0, 0.0, 0.0],
        description="Algae effect on RPOC, LPOC, DOC decay (day^-1.m3.g[C]^-1)",
    )
    knalg: Optional[list] = Field(
        [0.0, 0.0, 0.0],
        description="Algae effect on RPON, LPON, DON decay (day^-1.m3.g[C]^-1)",
    )
    kpalg: Optional[list] = Field(
        [0.0, 0.0, 0.0],
        description="Algae effect on RPOP, LPOP, DOP decay (day^-1.m3.g[C]^-1)",
    )
    trm: Optional[list] = Field(
        [20.0, 20.0, 20.0],
        description="Reference temperatures for (RPOM,LPOM,DOM) decay (°C)",
    )
    ktrm: Optional[list] = Field(
        [0.069, 0.069, 0.069],
        description="Temperature dependence for (RPOM,LPOM,DOM) decay (°C^-1)",
    )
    ksr0: Optional[list] = Field(
        [0.001, 0.001, 0.001], description="Decay rates of SRPOC, SRPON, SRPOP (day^-1)"
    )
    trsr: Optional[list] = Field(
        [20.0, 20.0, 20.0],
        description="Reference temperatures for (SRPOC,SRPON,SRPOP) decay (°C)",
    )
    ktrsr: Optional[list] = Field(
        [0.069, 0.069, 0.069],
        description="Temperature dependence for (SRPOC,SRPON,SRPOP) decay (°C^-1)",
    )
    kpip: Optional[float] = Field(0.0, description="Dissolution rate of PIP (day^-1)")
    kcd: Optional[float] = Field(
        1.0, description="Oxidation rate of COD at TRCOD (day^-1)"
    )
    trcod: Optional[float] = Field(
        20.0, description="Reference temperature for COD oxidation (°C)"
    )
    ktrcod: Optional[float] = Field(
        0.041, description="Temperature dependence for COD oxidation (°C^-1)"
    )
    khcod: Optional[float] = Field(
        1.5, description="COD half saturation for COD oxidation (mg[O2]/L)"
    )
    khn: Optional[list] = Field(
        [0.01, 0.01, 0.01],
        description="Nitrogen half saturation for three phytoplankton species (mg/L)",
    )
    khp: Optional[list] = Field(
        [0.001, 0.001, 0.001],
        description="Phosphorus half saturation for three phytoplankton species (mg/L)",
    )
    khsal: Optional[list] = Field(
        [1000000.0, 1000000.0, 0.1],
        description="Salinity when phytoplankton growth is halved for three species (PSU); (1e6: no salinity stress)",
    )
    c2chl: Optional[list] = Field(
        [0.059, 0.059, 0.059],
        description="Carbon to chlorophyll ratio for three phytoplankton species (g[C]/mg[Chl])",
    )
    n2c: Optional[list] = Field(
        [0.167, 0.167, 0.167],
        description="Nitrogen to carbon ratio for three phytoplankton species",
    )
    p2c: Optional[list] = Field(
        [0.02, 0.02, 0.02],
        description="Phosphorus to carbon ratio for three phytoplankton species",
    )
    o2c: Optional[float] = Field(
        2.67, description="Oxygen to carbon ratio in respiration"
    )
    o2n: Optional[float] = Field(
        4.33, description="Oxygen to ammonium ratio (g[O2]/g[NH4])"
    )
    dn2c: Optional[float] = Field(
        0.933,
        description="Mass of NO3 consumed per mass of DOC oxidized in denitrification (g[N]/g[C])",
    )
    an2c: Optional[float] = Field(
        0.5, description="Ratio of denitrification rate to oxic DOC respiration rate"
    )
    khdo: Optional[list] = Field(
        [0.5, 0.5, 0.5],
        description="DO half saturation for phytoplankton's DOC excretion (mg/L) for three species",
    )
    kpo4p: Optional[float] = Field(
        0.0, description="Coefficient relating PO4 sorption to TSS"
    )
    wrea: Optional[float] = Field(
        0.0, description="Baseline wind-induced reaeration coefficient for DO (day^-1)"
    )
    pbmin: Optional[list] = Field(
        [0.01, 0.01, 0.01],
        description="Minimum phytoplankton concentration (mg[C]/L) for three species",
    )
    dz_flux: Optional[list] = Field(
        [1.0, 1.0],
        description="Surface/bottom thickness (m) within which surface flux/bottom flux are redistributed",
    )

    @field_validator("gpm")
    @classmethod
    def validate_gpm(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and x > 0 for x in v)
        ):
            raise ValueError("GPM must be a list of 3 positive numbers")
        return v

    @field_validator("tgp")
    @classmethod
    def validate_tgp(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and 0 <= x <= 40 for x in v)
        ):
            raise ValueError("TGP must be a list of 3 numbers between 0 and 40")
        return v

    @field_validator("ktgp")
    @classmethod
    def validate_ktgp(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 6
            or not all(isinstance(x, (int, float)) and x > 0 for x in v)
        ):
            raise ValueError("KTGP must be a list of 6 positive numbers")
        return v

    @field_validator("mtr")
    @classmethod
    def validate_mtr(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and 0 <= x < 1 for x in v)
        ):
            raise ValueError("MTR must be a list of 3 numbers between 0 and 1")
        return v

    @field_validator("mtb")
    @classmethod
    def validate_mtb(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and x > 0 for x in v)
        ):
            raise ValueError("MTB must be a list of 3 positive numbers")
        return v

    @field_validator("tmt")
    @classmethod
    def validate_tmt(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and 0 <= x <= 40 for x in v)
        ):
            raise ValueError("TMT must be a list of 3 numbers between 0 and 40")
        return v

    @field_validator("ktmt")
    @classmethod
    def validate_ktmt(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and x > 0 for x in v)
        ):
            raise ValueError("KTMT must be a list of 3 positive numbers")
        return v

    @field_validator("fcp")
    @classmethod
    def validate_fcp(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 12
            or not all(isinstance(x, (int, float)) and 0 <= x <= 1 for x in v)
        ):
            raise ValueError("FCP must be a list of 12 numbers between 0 and 1")
        return v

    @field_validator("fnp")
    @classmethod
    def validate_fnp(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 15
            or not all(isinstance(x, (int, float)) and 0 <= x <= 1 for x in v)
        ):
            raise ValueError("FNP must be a list of 15 numbers between 0 and 1")
        return v

    @field_validator("fpp")
    @classmethod
    def validate_fpp(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 15
            or not all(isinstance(x, (int, float)) and 0 <= x <= 1 for x in v)
        ):
            raise ValueError("FPP must be a list of 15 numbers between 0 and 1")
        return v

    @field_validator("fcm")
    @classmethod
    def validate_fcm(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 12
            or not all(isinstance(x, (int, float)) and 0 <= x <= 1 for x in v)
        ):
            raise ValueError("FCM must be a list of 12 numbers between 0 and 1")
        return v

    @field_validator("fnm")
    @classmethod
    def validate_fnm(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 15
            or not all(isinstance(x, (int, float)) and 0 <= x <= 1 for x in v)
        ):
            raise ValueError("FNM must be a list of 15 numbers between 0 and 1")
        return v

    @field_validator("fpm")
    @classmethod
    def validate_fpm(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 15
            or not all(isinstance(x, (int, float)) and 0 <= x <= 1 for x in v)
        ):
            raise ValueError("FPM must be a list of 15 numbers between 0 and 1")
        return v

    @field_validator("nit")
    @classmethod
    def validate_nit(cls, v):
        if not isinstance(v, (int, float)) or v <= 0:
            raise ValueError("Nit must be a positive number")
        return v

    @field_validator("tnit")
    @classmethod
    def validate_tnit(cls, v):
        if not isinstance(v, (int, float)) or v < 0 or v > 40:
            raise ValueError("TNit must be a number between 0 and 40")
        return v

    @field_validator("ktnit")
    @classmethod
    def validate_ktnit(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 2
            or not all(isinstance(x, (int, float)) and x > 0 for x in v)
        ):
            raise ValueError("KTNit must be a list of 2 positive numbers")
        return v

    @field_validator("khdon")
    @classmethod
    def validate_khdon(cls, v):
        if not isinstance(v, (int, float)) or v <= 0:
            raise ValueError("KhDOn must be a positive number")
        return v

    @field_validator("khdoox")
    @classmethod
    def validate_khdoox(cls, v):
        if not isinstance(v, (int, float)) or v <= 0:
            raise ValueError("KhDOox must be a positive number")
        return v

    @field_validator("khno3dn")
    @classmethod
    def validate_khno3dn(cls, v):
        if not isinstance(v, (int, float)) or v <= 0:
            raise ValueError("KhNO3dn must be a positive number")
        return v

    @field_validator("kc0")
    @classmethod
    def validate_kc0(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and x > 0 for x in v)
        ):
            raise ValueError("KC0 must be a list of 3 positive numbers")
        return v

    @field_validator("kn0")
    @classmethod
    def validate_kn0(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and x > 0 for x in v)
        ):
            raise ValueError("KN0 must be a list of 3 positive numbers")
        return v

    @field_validator("kp0")
    @classmethod
    def validate_kp0(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and x > 0 for x in v)
        ):
            raise ValueError("KP0 must be a list of 3 positive numbers")
        return v

    @field_validator("kcalg")
    @classmethod
    def validate_kcalg(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and x >= 0 for x in v)
        ):
            raise ValueError("KCalg must be a list of 3 non-negative numbers")
        return v

    @field_validator("knalg")
    @classmethod
    def validate_knalg(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and x >= 0 for x in v)
        ):
            raise ValueError("KNalg must be a list of 3 non-negative numbers")
        return v

    @field_validator("kpalg")
    @classmethod
    def validate_kpalg(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and x >= 0 for x in v)
        ):
            raise ValueError("KPalg must be a list of 3 non-negative numbers")
        return v

    @field_validator("trm")
    @classmethod
    def validate_trm(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and 0 <= x <= 40 for x in v)
        ):
            raise ValueError("TRM must be a list of 3 numbers between 0 and 40")
        return v

    @field_validator("ktrm")
    @classmethod
    def validate_ktrm(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and x > 0 for x in v)
        ):
            raise ValueError("KTRM must be a list of 3 positive numbers")
        return v

    @field_validator("ksr0")
    @classmethod
    def validate_ksr0(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and x > 0 for x in v)
        ):
            raise ValueError("KSR0 must be a list of 3 positive numbers")
        return v

    @field_validator("trsr")
    @classmethod
    def validate_trsr(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and 0 <= x <= 40 for x in v)
        ):
            raise ValueError("TRSR must be a list of 3 numbers between 0 and 40")
        return v

    @field_validator("ktrsr")
    @classmethod
    def validate_ktrsr(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and x > 0 for x in v)
        ):
            raise ValueError("KTRSR must be a list of 3 positive numbers")
        return v

    @field_validator("kpip")
    @classmethod
    def validate_kpip(cls, v):
        if not isinstance(v, (int, float)) or v < 0:
            raise ValueError("KPIP must be a non-negative number")
        return v

    @field_validator("kcd")
    @classmethod
    def validate_kcd(cls, v):
        if not isinstance(v, (int, float)) or v <= 0:
            raise ValueError("KCD must be a positive number")
        return v

    @field_validator("trcod")
    @classmethod
    def validate_trcod(cls, v):
        if not isinstance(v, (int, float)) or v < 0 or v > 40:
            raise ValueError("TRCOD must be a number between 0 and 40")
        return v

    @field_validator("ktrcod")
    @classmethod
    def validate_ktrcod(cls, v):
        if not isinstance(v, (int, float)) or v <= 0:
            raise ValueError("KTRCOD must be a positive number")
        return v

    @field_validator("khcod")
    @classmethod
    def validate_khcod(cls, v):
        if not isinstance(v, (int, float)) or v <= 0:
            raise ValueError("KhCOD must be a positive number")
        return v

    @field_validator("khn")
    @classmethod
    def validate_khn(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and x > 0 for x in v)
        ):
            raise ValueError("KhN must be a list of 3 positive numbers")
        return v

    @field_validator("khp")
    @classmethod
    def validate_khp(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and x > 0 for x in v)
        ):
            raise ValueError("KhP must be a list of 3 positive numbers")
        return v

    @field_validator("khsal")
    @classmethod
    def validate_khsal(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and x > 0 for x in v)
        ):
            raise ValueError("KhSal must be a list of 3 positive numbers")
        return v

    @field_validator("c2chl")
    @classmethod
    def validate_c2chl(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and x > 0 for x in v)
        ):
            raise ValueError("c2chl must be a list of 3 positive numbers")
        return v

    @field_validator("n2c")
    @classmethod
    def validate_n2c(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and 0 < x < 1 for x in v)
        ):
            raise ValueError("n2c must be a list of 3 numbers between 0 and 1")
        return v

    @field_validator("p2c")
    @classmethod
    def validate_p2c(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and 0 < x < 1 for x in v)
        ):
            raise ValueError("p2c must be a list of 3 numbers between 0 and 1")
        return v

    @field_validator("o2c")
    @classmethod
    def validate_o2c(cls, v):
        if not isinstance(v, (int, float)) or v <= 0:
            raise ValueError("o2c must be a positive number")
        return v

    @field_validator("o2n")
    @classmethod
    def validate_o2n(cls, v):
        if not isinstance(v, (int, float)) or v <= 0:
            raise ValueError("o2n must be a positive number")
        return v

    @field_validator("dn2c")
    @classmethod
    def validate_dn2c(cls, v):
        if not isinstance(v, (int, float)) or v <= 0:
            raise ValueError("dn2c must be a positive number")
        return v

    @field_validator("an2c")
    @classmethod
    def validate_an2c(cls, v):
        if not isinstance(v, (int, float)) or v <= 0 or v > 1:
            raise ValueError("an2c must be a number between 0 and 1")
        return v

    @field_validator("khdo")
    @classmethod
    def validate_khdo(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and x > 0 for x in v)
        ):
            raise ValueError("KhDO must be a list of 3 positive numbers")
        return v

    @field_validator("kpo4p")
    @classmethod
    def validate_kpo4p(cls, v):
        if not isinstance(v, (int, float)) or v < 0:
            raise ValueError("KPO4p must be a non-negative number")
        return v

    @field_validator("wrea")
    @classmethod
    def validate_wrea(cls, v):
        if not isinstance(v, (int, float)) or v < 0:
            raise ValueError("WRea must be a non-negative number")
        return v

    @field_validator("pbmin")
    @classmethod
    def validate_pbmin(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 3
            or not all(isinstance(x, (int, float)) and x > 0 for x in v)
        ):
            raise ValueError("PBmin must be a list of 3 positive numbers")
        return v

    @field_validator("dz_flux")
    @classmethod
    def validate_dz_flux(cls, v):
        if (
            not isinstance(v, list)
            or len(v) != 2
            or not all(isinstance(x, (int, float)) and x > 0 for x in v)
        ):
            raise ValueError("dz_flux must be a list of 2 positive numbers")
        return v

    @model_validator(mode="after")
    def validate_fcp_sum(self):
        for i in range(3):
            if sum(self.fcp[i::3]) != 1:
                raise ValueError(f"Sum of FCP fractions for species {i+1} must equal 1")
        return self

    @model_validator(mode="after")
    def validate_fnp_sum(self):
        for i in range(3):
            if sum(self.fnp[i::3]) != 1:
                raise ValueError(f"Sum of FNP fractions for species {i+1} must equal 1")
        return self

    @model_validator(mode="after")
    def validate_fpp_sum(self):
        for i in range(3):
            if sum(self.fpp[i::3]) != 1:
                raise ValueError(f"Sum of FPP fractions for species {i+1} must equal 1")
        return self

    @model_validator(mode="after")
    def validate_fcm_sum(self):
        for i in range(3):
            if sum(self.fcm[i::3]) != 0.1:
                raise ValueError(
                    f"Sum of FCM fractions for species {i+1} must equal 0.1"
                )
        return self

    @model_validator(mode="after")
    def validate_fnm_sum(self):
        for i in range(5):
            if sum(self.fnm[i::3]) != 1:
                raise ValueError(f"Sum of FNM fractions for species {i+1} must equal 1")
        return self

    @model_validator(mode="after")
    def validate_fpm_sum(self):
        for i in range(5):
            if sum(self.fpm[i::3]) != 1:
                raise ValueError(f"Sum of FPM fractions for species {i+1} must equal 1")
        return self

Attributes

gpm class-attribute instance-attribute

gpm: Optional[list] = Field([2.5, 2.8, 3.5], description='Phytoplankton growth rates for three different species (day^-1)')

tgp class-attribute instance-attribute

tgp: Optional[list] = Field([15.0, 22.0, 27.0], description='Optimal temperatures for phytoplankton growth for three different species (°C)')

ktgp class-attribute instance-attribute

ktgp: Optional[list] = Field([0.005, 0.004, 0.003, 0.008, 0.006, 0.004], description='Temperature dependence for phytoplankton growth, dimensioned as (PB=1:3,1:2) (°C^-2)')

mtr class-attribute instance-attribute

mtr: Optional[list] = Field([0.0, 0.0, 0.0], description='Phytoplankton photorespiration coefficients for three species (0 < MTR < 1)')

mtb class-attribute instance-attribute

mtb: Optional[list] = Field([0.01, 0.02, 0.03], description='Phytoplankton metabolism rates for three species (day^-1)')

tmt class-attribute instance-attribute

tmt: Optional[list] = Field([20.0, 20.0, 20.0], description='Reference temperatures for phytoplankton metabolism for three species (°C)')

ktmt class-attribute instance-attribute

ktmt: Optional[list] = Field([0.0322, 0.0322, 0.0322], description='Temperature dependence for phytoplankton metabolism for three species (°C^-1)')

fcp class-attribute instance-attribute

fcp: Optional[list] = Field([0.35, 0.3, 0.2, 0.55, 0.5, 0.5, 0.1, 0.2, 0.3, 0.0, 0.0, 0.0], description='Fractions of phytoplankton carbon into (RPOC,LPOC,DOC,SRPOC) for three species')

fnp class-attribute instance-attribute

fnp: Optional[list] = Field([0.35, 0.35, 0.35, 0.5, 0.5, 0.5, 0.1, 0.1, 0.1, 0.05, 0.05, 0.05, 0.0, 0.0, 0.0], description='Fractions of phytoplankton nitrogen into (RPON,LPON,DON,NH4,SRPON) for three species')

fpp class-attribute instance-attribute

fpp: Optional[list] = Field([0.1, 0.1, 0.1, 0.2, 0.2, 0.2, 0.5, 0.5, 0.5, 0.2, 0.2, 0.2, 0.0, 0.0, 0.0], description='Fractions of phytoplankton Phosphorus into (RPOP,LPOP,DOP,PO4,SRPOP) for three species')

fcm class-attribute instance-attribute

fcm: Optional[list] = Field([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.1, 0.1, 0.1, 0.0, 0.0, 0.0], description='Fractions of phytoplankton metabolism carbon into (RPOC,LPOC,DOC,SRPOC) for three species')

fnm class-attribute instance-attribute

fnm: Optional[list] = Field([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], description='Fractions of phytoplankton metabolism nitrogen into (RPON,LPON,DON,NH4,SRPON) for three species')

fpm class-attribute instance-attribute

fpm: Optional[list] = Field([0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0], description='Fractions of phytoplankton metabolism phosphorus into (RPOP,LPOP,DOP,PO4,SRPOP) for three species')

nit class-attribute instance-attribute

nit: Optional[float] = Field(0.07, description='Maximum nitrification rate (day^-1)')

tnit class-attribute instance-attribute

tnit: Optional[float] = Field(27.0, description='Optimal temperature for nitrification (°C)')

ktnit class-attribute instance-attribute

ktnit: Optional[list] = Field([0.0045, 0.0045], description='Temperature dependence for nitrification (T<=TNit & T>TNit) (°C^-2)')

khdon class-attribute instance-attribute

khdon: Optional[float] = Field(1.0, description='Dissolved Oxygen half saturation for nitrification (mg/L)')

khdoox class-attribute instance-attribute

khdoox: Optional[float] = Field(0.5, description="Dissolved Oxygen half saturation for denitrification & DOC's oxic respiration (mg/L)")

khno3dn class-attribute instance-attribute

khno3dn: Optional[float] = Field(0.1, description='Nitrate half saturation for denitrification (mg/L)')

kc0 class-attribute instance-attribute

kc0: Optional[list] = Field([0.005, 0.075, 0.2], description='Minimum decay rates of RPOC, LPOC, DOC (day^-1)')

kn0 class-attribute instance-attribute

kn0: Optional[list] = Field([0.005, 0.075, 0.2], description='Minimum decay rates of RPON, LPON, DON (day^-1)')

kp0 class-attribute instance-attribute

kp0: Optional[list] = Field([0.005, 0.075, 0.2], description='Minimum decay rates of RPOP, LPOP, DOP (day^-1)')

kcalg class-attribute instance-attribute

kcalg: Optional[list] = Field([0.0, 0.0, 0.0], description='Algae effect on RPOC, LPOC, DOC decay (day^-1.m3.g[C]^-1)')

knalg class-attribute instance-attribute

knalg: Optional[list] = Field([0.0, 0.0, 0.0], description='Algae effect on RPON, LPON, DON decay (day^-1.m3.g[C]^-1)')

kpalg class-attribute instance-attribute

kpalg: Optional[list] = Field([0.0, 0.0, 0.0], description='Algae effect on RPOP, LPOP, DOP decay (day^-1.m3.g[C]^-1)')

trm class-attribute instance-attribute

trm: Optional[list] = Field([20.0, 20.0, 20.0], description='Reference temperatures for (RPOM,LPOM,DOM) decay (°C)')

ktrm class-attribute instance-attribute

ktrm: Optional[list] = Field([0.069, 0.069, 0.069], description='Temperature dependence for (RPOM,LPOM,DOM) decay (°C^-1)')

ksr0 class-attribute instance-attribute

ksr0: Optional[list] = Field([0.001, 0.001, 0.001], description='Decay rates of SRPOC, SRPON, SRPOP (day^-1)')

trsr class-attribute instance-attribute

trsr: Optional[list] = Field([20.0, 20.0, 20.0], description='Reference temperatures for (SRPOC,SRPON,SRPOP) decay (°C)')

ktrsr class-attribute instance-attribute

ktrsr: Optional[list] = Field([0.069, 0.069, 0.069], description='Temperature dependence for (SRPOC,SRPON,SRPOP) decay (°C^-1)')

kpip class-attribute instance-attribute

kpip: Optional[float] = Field(0.0, description='Dissolution rate of PIP (day^-1)')

kcd class-attribute instance-attribute

kcd: Optional[float] = Field(1.0, description='Oxidation rate of COD at TRCOD (day^-1)')

trcod class-attribute instance-attribute

trcod: Optional[float] = Field(20.0, description='Reference temperature for COD oxidation (°C)')

ktrcod class-attribute instance-attribute

ktrcod: Optional[float] = Field(0.041, description='Temperature dependence for COD oxidation (°C^-1)')

khcod class-attribute instance-attribute

khcod: Optional[float] = Field(1.5, description='COD half saturation for COD oxidation (mg[O2]/L)')

khn class-attribute instance-attribute

khn: Optional[list] = Field([0.01, 0.01, 0.01], description='Nitrogen half saturation for three phytoplankton species (mg/L)')

khp class-attribute instance-attribute

khp: Optional[list] = Field([0.001, 0.001, 0.001], description='Phosphorus half saturation for three phytoplankton species (mg/L)')

khsal class-attribute instance-attribute

khsal: Optional[list] = Field([1000000.0, 1000000.0, 0.1], description='Salinity when phytoplankton growth is halved for three species (PSU); (1e6: no salinity stress)')

c2chl class-attribute instance-attribute

c2chl: Optional[list] = Field([0.059, 0.059, 0.059], description='Carbon to chlorophyll ratio for three phytoplankton species (g[C]/mg[Chl])')

n2c class-attribute instance-attribute

n2c: Optional[list] = Field([0.167, 0.167, 0.167], description='Nitrogen to carbon ratio for three phytoplankton species')

p2c class-attribute instance-attribute

p2c: Optional[list] = Field([0.02, 0.02, 0.02], description='Phosphorus to carbon ratio for three phytoplankton species')

o2c class-attribute instance-attribute

o2c: Optional[float] = Field(2.67, description='Oxygen to carbon ratio in respiration')

o2n class-attribute instance-attribute

o2n: Optional[float] = Field(4.33, description='Oxygen to ammonium ratio (g[O2]/g[NH4])')

dn2c class-attribute instance-attribute

dn2c: Optional[float] = Field(0.933, description='Mass of NO3 consumed per mass of DOC oxidized in denitrification (g[N]/g[C])')

an2c class-attribute instance-attribute

an2c: Optional[float] = Field(0.5, description='Ratio of denitrification rate to oxic DOC respiration rate')

khdo class-attribute instance-attribute

khdo: Optional[list] = Field([0.5, 0.5, 0.5], description="DO half saturation for phytoplankton's DOC excretion (mg/L) for three species")

kpo4p class-attribute instance-attribute

kpo4p: Optional[float] = Field(0.0, description='Coefficient relating PO4 sorption to TSS')

wrea class-attribute instance-attribute

wrea: Optional[float] = Field(0.0, description='Baseline wind-induced reaeration coefficient for DO (day^-1)')

pbmin class-attribute instance-attribute

pbmin: Optional[list] = Field([0.01, 0.01, 0.01], description='Minimum phytoplankton concentration (mg[C]/L) for three species')

dz_flux class-attribute instance-attribute

dz_flux: Optional[list] = Field([1.0, 1.0], description='Surface/bottom thickness (m) within which surface flux/bottom flux are redistributed')

Functions

validate_gpm classmethod

validate_gpm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("gpm")
@classmethod
def validate_gpm(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and x > 0 for x in v)
    ):
        raise ValueError("GPM must be a list of 3 positive numbers")
    return v

validate_tgp classmethod

validate_tgp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("tgp")
@classmethod
def validate_tgp(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and 0 <= x <= 40 for x in v)
    ):
        raise ValueError("TGP must be a list of 3 numbers between 0 and 40")
    return v

validate_ktgp classmethod

validate_ktgp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ktgp")
@classmethod
def validate_ktgp(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 6
        or not all(isinstance(x, (int, float)) and x > 0 for x in v)
    ):
        raise ValueError("KTGP must be a list of 6 positive numbers")
    return v

validate_mtr classmethod

validate_mtr(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("mtr")
@classmethod
def validate_mtr(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and 0 <= x < 1 for x in v)
    ):
        raise ValueError("MTR must be a list of 3 numbers between 0 and 1")
    return v

validate_mtb classmethod

validate_mtb(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("mtb")
@classmethod
def validate_mtb(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and x > 0 for x in v)
    ):
        raise ValueError("MTB must be a list of 3 positive numbers")
    return v

validate_tmt classmethod

validate_tmt(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("tmt")
@classmethod
def validate_tmt(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and 0 <= x <= 40 for x in v)
    ):
        raise ValueError("TMT must be a list of 3 numbers between 0 and 40")
    return v

validate_ktmt classmethod

validate_ktmt(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ktmt")
@classmethod
def validate_ktmt(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and x > 0 for x in v)
    ):
        raise ValueError("KTMT must be a list of 3 positive numbers")
    return v

validate_fcp classmethod

validate_fcp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("fcp")
@classmethod
def validate_fcp(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 12
        or not all(isinstance(x, (int, float)) and 0 <= x <= 1 for x in v)
    ):
        raise ValueError("FCP must be a list of 12 numbers between 0 and 1")
    return v

validate_fnp classmethod

validate_fnp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("fnp")
@classmethod
def validate_fnp(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 15
        or not all(isinstance(x, (int, float)) and 0 <= x <= 1 for x in v)
    ):
        raise ValueError("FNP must be a list of 15 numbers between 0 and 1")
    return v

validate_fpp classmethod

validate_fpp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("fpp")
@classmethod
def validate_fpp(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 15
        or not all(isinstance(x, (int, float)) and 0 <= x <= 1 for x in v)
    ):
        raise ValueError("FPP must be a list of 15 numbers between 0 and 1")
    return v

validate_fcm classmethod

validate_fcm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("fcm")
@classmethod
def validate_fcm(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 12
        or not all(isinstance(x, (int, float)) and 0 <= x <= 1 for x in v)
    ):
        raise ValueError("FCM must be a list of 12 numbers between 0 and 1")
    return v

validate_fnm classmethod

validate_fnm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("fnm")
@classmethod
def validate_fnm(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 15
        or not all(isinstance(x, (int, float)) and 0 <= x <= 1 for x in v)
    ):
        raise ValueError("FNM must be a list of 15 numbers between 0 and 1")
    return v

validate_fpm classmethod

validate_fpm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("fpm")
@classmethod
def validate_fpm(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 15
        or not all(isinstance(x, (int, float)) and 0 <= x <= 1 for x in v)
    ):
        raise ValueError("FPM must be a list of 15 numbers between 0 and 1")
    return v

validate_nit classmethod

validate_nit(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("nit")
@classmethod
def validate_nit(cls, v):
    if not isinstance(v, (int, float)) or v <= 0:
        raise ValueError("Nit must be a positive number")
    return v

validate_tnit classmethod

validate_tnit(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("tnit")
@classmethod
def validate_tnit(cls, v):
    if not isinstance(v, (int, float)) or v < 0 or v > 40:
        raise ValueError("TNit must be a number between 0 and 40")
    return v

validate_ktnit classmethod

validate_ktnit(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ktnit")
@classmethod
def validate_ktnit(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 2
        or not all(isinstance(x, (int, float)) and x > 0 for x in v)
    ):
        raise ValueError("KTNit must be a list of 2 positive numbers")
    return v

validate_khdon classmethod

validate_khdon(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("khdon")
@classmethod
def validate_khdon(cls, v):
    if not isinstance(v, (int, float)) or v <= 0:
        raise ValueError("KhDOn must be a positive number")
    return v

validate_khdoox classmethod

validate_khdoox(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("khdoox")
@classmethod
def validate_khdoox(cls, v):
    if not isinstance(v, (int, float)) or v <= 0:
        raise ValueError("KhDOox must be a positive number")
    return v

validate_khno3dn classmethod

validate_khno3dn(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("khno3dn")
@classmethod
def validate_khno3dn(cls, v):
    if not isinstance(v, (int, float)) or v <= 0:
        raise ValueError("KhNO3dn must be a positive number")
    return v

validate_kc0 classmethod

validate_kc0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("kc0")
@classmethod
def validate_kc0(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and x > 0 for x in v)
    ):
        raise ValueError("KC0 must be a list of 3 positive numbers")
    return v

validate_kn0 classmethod

validate_kn0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("kn0")
@classmethod
def validate_kn0(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and x > 0 for x in v)
    ):
        raise ValueError("KN0 must be a list of 3 positive numbers")
    return v

validate_kp0 classmethod

validate_kp0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("kp0")
@classmethod
def validate_kp0(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and x > 0 for x in v)
    ):
        raise ValueError("KP0 must be a list of 3 positive numbers")
    return v

validate_kcalg classmethod

validate_kcalg(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("kcalg")
@classmethod
def validate_kcalg(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and x >= 0 for x in v)
    ):
        raise ValueError("KCalg must be a list of 3 non-negative numbers")
    return v

validate_knalg classmethod

validate_knalg(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("knalg")
@classmethod
def validate_knalg(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and x >= 0 for x in v)
    ):
        raise ValueError("KNalg must be a list of 3 non-negative numbers")
    return v

validate_kpalg classmethod

validate_kpalg(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("kpalg")
@classmethod
def validate_kpalg(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and x >= 0 for x in v)
    ):
        raise ValueError("KPalg must be a list of 3 non-negative numbers")
    return v

validate_trm classmethod

validate_trm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("trm")
@classmethod
def validate_trm(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and 0 <= x <= 40 for x in v)
    ):
        raise ValueError("TRM must be a list of 3 numbers between 0 and 40")
    return v

validate_ktrm classmethod

validate_ktrm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ktrm")
@classmethod
def validate_ktrm(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and x > 0 for x in v)
    ):
        raise ValueError("KTRM must be a list of 3 positive numbers")
    return v

validate_ksr0 classmethod

validate_ksr0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ksr0")
@classmethod
def validate_ksr0(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and x > 0 for x in v)
    ):
        raise ValueError("KSR0 must be a list of 3 positive numbers")
    return v

validate_trsr classmethod

validate_trsr(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("trsr")
@classmethod
def validate_trsr(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and 0 <= x <= 40 for x in v)
    ):
        raise ValueError("TRSR must be a list of 3 numbers between 0 and 40")
    return v

validate_ktrsr classmethod

validate_ktrsr(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ktrsr")
@classmethod
def validate_ktrsr(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and x > 0 for x in v)
    ):
        raise ValueError("KTRSR must be a list of 3 positive numbers")
    return v

validate_kpip classmethod

validate_kpip(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("kpip")
@classmethod
def validate_kpip(cls, v):
    if not isinstance(v, (int, float)) or v < 0:
        raise ValueError("KPIP must be a non-negative number")
    return v

validate_kcd classmethod

validate_kcd(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("kcd")
@classmethod
def validate_kcd(cls, v):
    if not isinstance(v, (int, float)) or v <= 0:
        raise ValueError("KCD must be a positive number")
    return v

validate_trcod classmethod

validate_trcod(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("trcod")
@classmethod
def validate_trcod(cls, v):
    if not isinstance(v, (int, float)) or v < 0 or v > 40:
        raise ValueError("TRCOD must be a number between 0 and 40")
    return v

validate_ktrcod classmethod

validate_ktrcod(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ktrcod")
@classmethod
def validate_ktrcod(cls, v):
    if not isinstance(v, (int, float)) or v <= 0:
        raise ValueError("KTRCOD must be a positive number")
    return v

validate_khcod classmethod

validate_khcod(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("khcod")
@classmethod
def validate_khcod(cls, v):
    if not isinstance(v, (int, float)) or v <= 0:
        raise ValueError("KhCOD must be a positive number")
    return v

validate_khn classmethod

validate_khn(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("khn")
@classmethod
def validate_khn(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and x > 0 for x in v)
    ):
        raise ValueError("KhN must be a list of 3 positive numbers")
    return v

validate_khp classmethod

validate_khp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("khp")
@classmethod
def validate_khp(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and x > 0 for x in v)
    ):
        raise ValueError("KhP must be a list of 3 positive numbers")
    return v

validate_khsal classmethod

validate_khsal(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("khsal")
@classmethod
def validate_khsal(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and x > 0 for x in v)
    ):
        raise ValueError("KhSal must be a list of 3 positive numbers")
    return v

validate_c2chl classmethod

validate_c2chl(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("c2chl")
@classmethod
def validate_c2chl(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and x > 0 for x in v)
    ):
        raise ValueError("c2chl must be a list of 3 positive numbers")
    return v

validate_n2c classmethod

validate_n2c(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("n2c")
@classmethod
def validate_n2c(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and 0 < x < 1 for x in v)
    ):
        raise ValueError("n2c must be a list of 3 numbers between 0 and 1")
    return v

validate_p2c classmethod

validate_p2c(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("p2c")
@classmethod
def validate_p2c(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and 0 < x < 1 for x in v)
    ):
        raise ValueError("p2c must be a list of 3 numbers between 0 and 1")
    return v

validate_o2c classmethod

validate_o2c(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("o2c")
@classmethod
def validate_o2c(cls, v):
    if not isinstance(v, (int, float)) or v <= 0:
        raise ValueError("o2c must be a positive number")
    return v

validate_o2n classmethod

validate_o2n(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("o2n")
@classmethod
def validate_o2n(cls, v):
    if not isinstance(v, (int, float)) or v <= 0:
        raise ValueError("o2n must be a positive number")
    return v

validate_dn2c classmethod

validate_dn2c(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("dn2c")
@classmethod
def validate_dn2c(cls, v):
    if not isinstance(v, (int, float)) or v <= 0:
        raise ValueError("dn2c must be a positive number")
    return v

validate_an2c classmethod

validate_an2c(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("an2c")
@classmethod
def validate_an2c(cls, v):
    if not isinstance(v, (int, float)) or v <= 0 or v > 1:
        raise ValueError("an2c must be a number between 0 and 1")
    return v

validate_khdo classmethod

validate_khdo(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("khdo")
@classmethod
def validate_khdo(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and x > 0 for x in v)
    ):
        raise ValueError("KhDO must be a list of 3 positive numbers")
    return v

validate_kpo4p classmethod

validate_kpo4p(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("kpo4p")
@classmethod
def validate_kpo4p(cls, v):
    if not isinstance(v, (int, float)) or v < 0:
        raise ValueError("KPO4p must be a non-negative number")
    return v

validate_wrea classmethod

validate_wrea(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("wrea")
@classmethod
def validate_wrea(cls, v):
    if not isinstance(v, (int, float)) or v < 0:
        raise ValueError("WRea must be a non-negative number")
    return v

validate_pbmin classmethod

validate_pbmin(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("pbmin")
@classmethod
def validate_pbmin(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 3
        or not all(isinstance(x, (int, float)) and x > 0 for x in v)
    ):
        raise ValueError("PBmin must be a list of 3 positive numbers")
    return v

validate_dz_flux classmethod

validate_dz_flux(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("dz_flux")
@classmethod
def validate_dz_flux(cls, v):
    if (
        not isinstance(v, list)
        or len(v) != 2
        or not all(isinstance(x, (int, float)) and x > 0 for x in v)
    ):
        raise ValueError("dz_flux must be a list of 2 positive numbers")
    return v

validate_fcp_sum

validate_fcp_sum()
Source code in rompy_schism/namelists/icm.py
@model_validator(mode="after")
def validate_fcp_sum(self):
    for i in range(3):
        if sum(self.fcp[i::3]) != 1:
            raise ValueError(f"Sum of FCP fractions for species {i+1} must equal 1")
    return self

validate_fnp_sum

validate_fnp_sum()
Source code in rompy_schism/namelists/icm.py
@model_validator(mode="after")
def validate_fnp_sum(self):
    for i in range(3):
        if sum(self.fnp[i::3]) != 1:
            raise ValueError(f"Sum of FNP fractions for species {i+1} must equal 1")
    return self

validate_fpp_sum

validate_fpp_sum()
Source code in rompy_schism/namelists/icm.py
@model_validator(mode="after")
def validate_fpp_sum(self):
    for i in range(3):
        if sum(self.fpp[i::3]) != 1:
            raise ValueError(f"Sum of FPP fractions for species {i+1} must equal 1")
    return self

validate_fcm_sum

validate_fcm_sum()
Source code in rompy_schism/namelists/icm.py
@model_validator(mode="after")
def validate_fcm_sum(self):
    for i in range(3):
        if sum(self.fcm[i::3]) != 0.1:
            raise ValueError(
                f"Sum of FCM fractions for species {i+1} must equal 0.1"
            )
    return self

validate_fnm_sum

validate_fnm_sum()
Source code in rompy_schism/namelists/icm.py
@model_validator(mode="after")
def validate_fnm_sum(self):
    for i in range(5):
        if sum(self.fnm[i::3]) != 1:
            raise ValueError(f"Sum of FNM fractions for species {i+1} must equal 1")
    return self

validate_fpm_sum

validate_fpm_sum()
Source code in rompy_schism/namelists/icm.py
@model_validator(mode="after")
def validate_fpm_sum(self):
    for i in range(5):
        if sum(self.fpm[i::3]) != 1:
            raise ValueError(f"Sum of FPM fractions for species {i+1} must equal 1")
    return self

Ero

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/icm.py
class Ero(NamelistBaseModel):
    ierosion: Optional[int] = Field(
        0,
        description="Determines the type of benthic erosion flux to be modeled. 0: No erosion, 1: H2S flux, 2: POC (Particulate Organic Carbon) flux, 3: H2S flux (alternative method).",
    )

    @field_validator("ierosion")
    @classmethod
    def validate_ierosion(cls, v):
        if v not in [0, 1, 2, 3]:
            raise ValueError("ierosion must be 0, 1, 2, or 3")
        return v

Attributes

ierosion class-attribute instance-attribute

ierosion: Optional[int] = Field(0, description='Determines the type of benthic erosion flux to be modeled. 0: No erosion, 1: H2S flux, 2: POC (Particulate Organic Carbon) flux, 3: H2S flux (alternative method).')

Functions

validate_ierosion classmethod

validate_ierosion(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ierosion")
@classmethod
def validate_ierosion(cls, v):
    if v not in [0, 1, 2, 3]:
        raise ValueError("ierosion must be 0, 1, 2, or 3")
    return v

Marco

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/icm.py
class Marco(NamelistBaseModel):
    nsub: Optional[int] = Field(1, description="Number of subcycles in ICM kinetics")
    ike: Optional[int] = Field(
        0,
        description="Option for computing light attenuation coefficients. 0: Ke=Ke0+KeC*Chl+KeS*(tss2c*POC), 1: Ke=Ke0+KeC*Chl+KeS*TSS, 2: Ke=Ke0+KeC*Chl+KeSalt*Salt",
    )
    ke0: Optional[float] = Field(
        0.26, description="Background light extinction coefficient (1/m)"
    )
    kec: Optional[float] = Field(
        0.017, description="Light attenuation due to chlorophyll"
    )
    kes: Optional[float] = Field(0.07, description="Light attenuation due to TSS")
    kesalt: Optional[float] = Field(
        -0.02, description="Light attenuation due to CDOM (related to salinity)"
    )
    tss2c: Optional[float] = Field(6.0, description="TSS to carbon ratio")
    ilight: Optional[int] = Field(
        0,
        description="Option for computing light limitation factor. 0: Carl Cerco method (unit: E/m^2)",
    )
    alpha: Optional[list] = Field(
        [8.0, 8.0, 8.0],
        description="Initial slope of P-I curve (g[C]*m2/g[Chl]/E) for each phytoplankton group",
    )
    ipr: Optional[int] = Field(
        1,
        description="Option for phytoplankton predation term. 0: linear formulation, 1: quadratic",
    )
    prr: Optional[list] = Field(
        [0.1, 0.2, 0.05],
        description="Predation rate by higher trophic level (day-1 or day-1.g-1.m3) for each phytoplankton group",
    )
    wqc0: Optional[list] = Field(
        [
            1.0,
            0.5,
            0.05,
            1.0,
            0.5,
            0.5,
            0.15,
            0.15,
            0.05,
            0.01,
            0.05,
            0.005,
            0.005,
            0.01,
            0.05,
            0.0,
            12.0,
        ],
        description="Initial values for ICM state variables",
    )
    wsp: Optional[list] = Field(
        [
            0.3,
            0.1,
            0.0,
            0.25,
            0.25,
            0.0,
            0.25,
            0.25,
            0.0,
            0.0,
            0.0,
            0.25,
            0.25,
            0.0,
            1.0,
            0.0,
            0.0,
        ],
        description="Settling velocity for ICM state variables (m.day-1)",
    )
    wspn: Optional[list] = Field(
        [
            0.3,
            0.1,
            0.0,
            0.25,
            0.25,
            0.0,
            0.25,
            0.25,
            0.0,
            0.0,
            0.0,
            0.25,
            0.25,
            0.0,
            1.0,
            0.0,
            0.0,
        ],
        description="Net settling velocity for ICM state variables (m.day-1)",
    )
    isilica: Optional[int] = Field(0, description="Silica model switch. 0: OFF, 1: ON")
    izb: Optional[int] = Field(
        0,
        description="Zooplankton dynamics switch. 0: don't use, 1: use zooplankton dynamics",
    )
    iph: Optional[int] = Field(0, description="PH model switch. 0: OFF, 1: ON")
    icbp: Optional[int] = Field(
        0, description="Chesapeake Bay Program Model switch. 0: OFF, 1: ON"
    )
    isav_icm: Optional[int] = Field(
        0, description="Submerged Aquatic Vegetation switch. 0: OFF, 1: ON"
    )
    iveg_icm: Optional[int] = Field(
        0, description="Intertidal vegetation switch. 0: OFF, 1: ON"
    )
    ised: Optional[int] = Field(
        1, description="Sediment module switch. 0: OFF, 1: Use sediment flux model"
    )
    iba: Optional[int] = Field(0, description="Benthic Algae switch. 0: OFF, 1: ON")
    irad: Optional[int] = Field(
        0,
        description="Solar radiation option. 0: short wave from sflux, 1: short wave from ICM_rad.th.nc",
    )
    isflux: Optional[int] = Field(
        0,
        description="Atmospheric fluxes option. 0: OFF, 1: additional nutrient fluxes from ICM_sflux.th.nc",
    )
    ibflux: Optional[int] = Field(
        0,
        description="Bottom fluxes option. 0: OFF, 1: additional nutrient fluxes from ICM_bflux.th.nc",
    )
    iout_icm: Optional[int] = Field(
        0,
        description="ICM station outputs switch. 0: OFF, 1: ON (requires istation.in with *.bp format)",
    )
    nspool_icm: Optional[int] = Field(
        24, description="Output frequency for ICM station outputs"
    )
    ilimit: Optional[int] = Field(
        0,
        description="Option for nutrient limitation on phytoplankton growth. 0: f=min[f(N),f(P)]*f(I), 1: f=min[f(N),f(P),f(I)]",
    )
    idry_icm: Optional[int] = Field(
        0,
        description="Shallow kinetic biochemical process option. 0: jump dry elements, keep last wet value, 1: turn on shallow kinetic biochemical process",
    )

    @field_validator("nsub")
    @classmethod
    def validate_nsub(cls, v):
        if v < 1:
            raise ValueError("nsub must be a positive integer")
        return v

    @field_validator("ike")
    @classmethod
    def validate_ike(cls, v):
        if v not in [0, 1, 2]:
            raise ValueError("ike must be 0, 1, or 2")
        return v

    @field_validator("ke0")
    @classmethod
    def validate_ke0(cls, v):
        if v < 0:
            raise ValueError("ke0 must be non-negative")
        return v

    @field_validator("kec")
    @classmethod
    def validate_kec(cls, v):
        if v < 0:
            raise ValueError("kec must be non-negative")
        return v

    @field_validator("kes")
    @classmethod
    def validate_kes(cls, v):
        if v < 0:
            raise ValueError("kes must be non-negative")
        return v

    @field_validator("kesalt")
    @classmethod
    def validate_kesalt(cls, v):
        return v

    @field_validator("tss2c")
    @classmethod
    def validate_tss2c(cls, v):
        if v <= 0:
            raise ValueError("tss2c must be positive")
        return v

    @field_validator("ilight")
    @classmethod
    def validate_ilight(cls, v):
        if v != 0:
            raise ValueError("ilight must be 0")
        return v

    @field_validator("alpha")
    @classmethod
    def validate_alpha(cls, v):
        if len(v) != 3 or any(x <= 0 for x in v):
            raise ValueError("alpha must be a list of 3 positive values")
        return v

    @field_validator("ipr")
    @classmethod
    def validate_ipr(cls, v):
        if v not in [0, 1]:
            raise ValueError("ipr must be 0 or 1")
        return v

    @field_validator("prr")
    @classmethod
    def validate_prr(cls, v):
        if len(v) != 3 or any(x < 0 for x in v):
            raise ValueError("prr must be a list of 3 non-negative values")
        return v

    @field_validator("wqc0")
    @classmethod
    def validate_wqc0(cls, v):
        if len(v) != 16 or any(x < 0 for x in v):
            raise ValueError("wqc0 must be a list of 16 non-negative values")
        return v

    @field_validator("wsp")
    @classmethod
    def validate_wsp(cls, v):
        if len(v) != 16:
            raise ValueError("wsp must be a list of 16 values")
        return v

    @field_validator("wspn")
    @classmethod
    def validate_wspn(cls, v):
        if len(v) != 16:
            raise ValueError("wspn must be a list of 16 values")
        return v

    @field_validator("isilica")
    @classmethod
    def validate_isilica(cls, v):
        if v not in [0, 1]:
            raise ValueError("isilica must be 0 or 1")
        return v

    @field_validator("izb")
    @classmethod
    def validate_izb(cls, v):
        if v not in [0, 1]:
            raise ValueError("izb must be 0 or 1")
        return v

    @field_validator("iph")
    @classmethod
    def validate_iph(cls, v):
        if v not in [0, 1]:
            raise ValueError("iph must be 0 or 1")
        return v

    @field_validator("icbp")
    @classmethod
    def validate_icbp(cls, v):
        if v not in [0, 1]:
            raise ValueError("icbp must be 0 or 1")
        return v

    @field_validator("isav_icm")
    @classmethod
    def validate_isav_icm(cls, v):
        if v not in [0, 1]:
            raise ValueError("isav_icm must be 0 or 1")
        return v

    @field_validator("iveg_icm")
    @classmethod
    def validate_iveg_icm(cls, v):
        if v not in [0, 1]:
            raise ValueError("iveg_icm must be 0 or 1")
        return v

    @field_validator("ised")
    @classmethod
    def validate_ised(cls, v):
        if v not in [0, 1]:
            raise ValueError("ised must be 0 or 1")
        return v

    @field_validator("iba")
    @classmethod
    def validate_iba(cls, v):
        if v not in [0, 1]:
            raise ValueError("iba must be 0 or 1")
        return v

    @field_validator("irad")
    @classmethod
    def validate_irad(cls, v):
        if v not in [0, 1]:
            raise ValueError("irad must be 0 or 1")
        return v

    @field_validator("isflux")
    @classmethod
    def validate_isflux(cls, v):
        if v not in [0, 1]:
            raise ValueError("isflux must be 0 or 1")
        return v

    @field_validator("ibflux")
    @classmethod
    def validate_ibflux(cls, v):
        if v not in [0, 1]:
            raise ValueError("ibflux must be 0 or 1")
        return v

    @field_validator("iout_icm")
    @classmethod
    def validate_iout_icm(cls, v):
        if v not in [0, 1]:
            raise ValueError("iout_icm must be 0 or 1")
        return v

    @field_validator("nspool_icm")
    @classmethod
    def validate_nspool_icm(cls, v):
        if v <= 0:
            raise ValueError("nspool_icm must be positive")
        return v

    @field_validator("ilimit")
    @classmethod
    def validate_ilimit(cls, v):
        if v not in [0, 1]:
            raise ValueError("ilimit must be 0 or 1")
        return v

    @field_validator("idry_icm")
    @classmethod
    def validate_idry_icm(cls, v):
        if v not in [0, 1]:
            raise ValueError("idry_icm must be 0 or 1")
        return v

Attributes

nsub class-attribute instance-attribute

nsub: Optional[int] = Field(1, description='Number of subcycles in ICM kinetics')

ike class-attribute instance-attribute

ike: Optional[int] = Field(0, description='Option for computing light attenuation coefficients. 0: Ke=Ke0+KeC*Chl+KeS*(tss2c*POC), 1: Ke=Ke0+KeC*Chl+KeS*TSS, 2: Ke=Ke0+KeC*Chl+KeSalt*Salt')

ke0 class-attribute instance-attribute

ke0: Optional[float] = Field(0.26, description='Background light extinction coefficient (1/m)')

kec class-attribute instance-attribute

kec: Optional[float] = Field(0.017, description='Light attenuation due to chlorophyll')

kes class-attribute instance-attribute

kes: Optional[float] = Field(0.07, description='Light attenuation due to TSS')

kesalt class-attribute instance-attribute

kesalt: Optional[float] = Field(-0.02, description='Light attenuation due to CDOM (related to salinity)')

tss2c class-attribute instance-attribute

tss2c: Optional[float] = Field(6.0, description='TSS to carbon ratio')

ilight class-attribute instance-attribute

ilight: Optional[int] = Field(0, description='Option for computing light limitation factor. 0: Carl Cerco method (unit: E/m^2)')

alpha class-attribute instance-attribute

alpha: Optional[list] = Field([8.0, 8.0, 8.0], description='Initial slope of P-I curve (g[C]*m2/g[Chl]/E) for each phytoplankton group')

ipr class-attribute instance-attribute

ipr: Optional[int] = Field(1, description='Option for phytoplankton predation term. 0: linear formulation, 1: quadratic')

prr class-attribute instance-attribute

prr: Optional[list] = Field([0.1, 0.2, 0.05], description='Predation rate by higher trophic level (day-1 or day-1.g-1.m3) for each phytoplankton group')

wqc0 class-attribute instance-attribute

wqc0: Optional[list] = Field([1.0, 0.5, 0.05, 1.0, 0.5, 0.5, 0.15, 0.15, 0.05, 0.01, 0.05, 0.005, 0.005, 0.01, 0.05, 0.0, 12.0], description='Initial values for ICM state variables')

wsp class-attribute instance-attribute

wsp: Optional[list] = Field([0.3, 0.1, 0.0, 0.25, 0.25, 0.0, 0.25, 0.25, 0.0, 0.0, 0.0, 0.25, 0.25, 0.0, 1.0, 0.0, 0.0], description='Settling velocity for ICM state variables (m.day-1)')

wspn class-attribute instance-attribute

wspn: Optional[list] = Field([0.3, 0.1, 0.0, 0.25, 0.25, 0.0, 0.25, 0.25, 0.0, 0.0, 0.0, 0.25, 0.25, 0.0, 1.0, 0.0, 0.0], description='Net settling velocity for ICM state variables (m.day-1)')

isilica class-attribute instance-attribute

isilica: Optional[int] = Field(0, description='Silica model switch. 0: OFF, 1: ON')

izb class-attribute instance-attribute

izb: Optional[int] = Field(0, description="Zooplankton dynamics switch. 0: don't use, 1: use zooplankton dynamics")

iph class-attribute instance-attribute

iph: Optional[int] = Field(0, description='PH model switch. 0: OFF, 1: ON')

icbp class-attribute instance-attribute

icbp: Optional[int] = Field(0, description='Chesapeake Bay Program Model switch. 0: OFF, 1: ON')

isav_icm class-attribute instance-attribute

isav_icm: Optional[int] = Field(0, description='Submerged Aquatic Vegetation switch. 0: OFF, 1: ON')

iveg_icm class-attribute instance-attribute

iveg_icm: Optional[int] = Field(0, description='Intertidal vegetation switch. 0: OFF, 1: ON')

ised class-attribute instance-attribute

ised: Optional[int] = Field(1, description='Sediment module switch. 0: OFF, 1: Use sediment flux model')

iba class-attribute instance-attribute

iba: Optional[int] = Field(0, description='Benthic Algae switch. 0: OFF, 1: ON')

irad class-attribute instance-attribute

irad: Optional[int] = Field(0, description='Solar radiation option. 0: short wave from sflux, 1: short wave from ICM_rad.th.nc')

isflux class-attribute instance-attribute

isflux: Optional[int] = Field(0, description='Atmospheric fluxes option. 0: OFF, 1: additional nutrient fluxes from ICM_sflux.th.nc')

ibflux class-attribute instance-attribute

ibflux: Optional[int] = Field(0, description='Bottom fluxes option. 0: OFF, 1: additional nutrient fluxes from ICM_bflux.th.nc')

iout_icm class-attribute instance-attribute

iout_icm: Optional[int] = Field(0, description='ICM station outputs switch. 0: OFF, 1: ON (requires istation.in with *.bp format)')

nspool_icm class-attribute instance-attribute

nspool_icm: Optional[int] = Field(24, description='Output frequency for ICM station outputs')

ilimit class-attribute instance-attribute

ilimit: Optional[int] = Field(0, description='Option for nutrient limitation on phytoplankton growth. 0: f=min[f(N),f(P)]*f(I), 1: f=min[f(N),f(P),f(I)]')

idry_icm class-attribute instance-attribute

idry_icm: Optional[int] = Field(0, description='Shallow kinetic biochemical process option. 0: jump dry elements, keep last wet value, 1: turn on shallow kinetic biochemical process')

Functions

validate_nsub classmethod

validate_nsub(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("nsub")
@classmethod
def validate_nsub(cls, v):
    if v < 1:
        raise ValueError("nsub must be a positive integer")
    return v

validate_ike classmethod

validate_ike(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ike")
@classmethod
def validate_ike(cls, v):
    if v not in [0, 1, 2]:
        raise ValueError("ike must be 0, 1, or 2")
    return v

validate_ke0 classmethod

validate_ke0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ke0")
@classmethod
def validate_ke0(cls, v):
    if v < 0:
        raise ValueError("ke0 must be non-negative")
    return v

validate_kec classmethod

validate_kec(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("kec")
@classmethod
def validate_kec(cls, v):
    if v < 0:
        raise ValueError("kec must be non-negative")
    return v

validate_kes classmethod

validate_kes(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("kes")
@classmethod
def validate_kes(cls, v):
    if v < 0:
        raise ValueError("kes must be non-negative")
    return v

validate_kesalt classmethod

validate_kesalt(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("kesalt")
@classmethod
def validate_kesalt(cls, v):
    return v

validate_tss2c classmethod

validate_tss2c(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("tss2c")
@classmethod
def validate_tss2c(cls, v):
    if v <= 0:
        raise ValueError("tss2c must be positive")
    return v

validate_ilight classmethod

validate_ilight(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ilight")
@classmethod
def validate_ilight(cls, v):
    if v != 0:
        raise ValueError("ilight must be 0")
    return v

validate_alpha classmethod

validate_alpha(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("alpha")
@classmethod
def validate_alpha(cls, v):
    if len(v) != 3 or any(x <= 0 for x in v):
        raise ValueError("alpha must be a list of 3 positive values")
    return v

validate_ipr classmethod

validate_ipr(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ipr")
@classmethod
def validate_ipr(cls, v):
    if v not in [0, 1]:
        raise ValueError("ipr must be 0 or 1")
    return v

validate_prr classmethod

validate_prr(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("prr")
@classmethod
def validate_prr(cls, v):
    if len(v) != 3 or any(x < 0 for x in v):
        raise ValueError("prr must be a list of 3 non-negative values")
    return v

validate_wqc0 classmethod

validate_wqc0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("wqc0")
@classmethod
def validate_wqc0(cls, v):
    if len(v) != 16 or any(x < 0 for x in v):
        raise ValueError("wqc0 must be a list of 16 non-negative values")
    return v

validate_wsp classmethod

validate_wsp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("wsp")
@classmethod
def validate_wsp(cls, v):
    if len(v) != 16:
        raise ValueError("wsp must be a list of 16 values")
    return v

validate_wspn classmethod

validate_wspn(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("wspn")
@classmethod
def validate_wspn(cls, v):
    if len(v) != 16:
        raise ValueError("wspn must be a list of 16 values")
    return v

validate_isilica classmethod

validate_isilica(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("isilica")
@classmethod
def validate_isilica(cls, v):
    if v not in [0, 1]:
        raise ValueError("isilica must be 0 or 1")
    return v

validate_izb classmethod

validate_izb(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("izb")
@classmethod
def validate_izb(cls, v):
    if v not in [0, 1]:
        raise ValueError("izb must be 0 or 1")
    return v

validate_iph classmethod

validate_iph(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("iph")
@classmethod
def validate_iph(cls, v):
    if v not in [0, 1]:
        raise ValueError("iph must be 0 or 1")
    return v

validate_icbp classmethod

validate_icbp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("icbp")
@classmethod
def validate_icbp(cls, v):
    if v not in [0, 1]:
        raise ValueError("icbp must be 0 or 1")
    return v

validate_isav_icm classmethod

validate_isav_icm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("isav_icm")
@classmethod
def validate_isav_icm(cls, v):
    if v not in [0, 1]:
        raise ValueError("isav_icm must be 0 or 1")
    return v

validate_iveg_icm classmethod

validate_iveg_icm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("iveg_icm")
@classmethod
def validate_iveg_icm(cls, v):
    if v not in [0, 1]:
        raise ValueError("iveg_icm must be 0 or 1")
    return v

validate_ised classmethod

validate_ised(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ised")
@classmethod
def validate_ised(cls, v):
    if v not in [0, 1]:
        raise ValueError("ised must be 0 or 1")
    return v

validate_iba classmethod

validate_iba(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("iba")
@classmethod
def validate_iba(cls, v):
    if v not in [0, 1]:
        raise ValueError("iba must be 0 or 1")
    return v

validate_irad classmethod

validate_irad(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("irad")
@classmethod
def validate_irad(cls, v):
    if v not in [0, 1]:
        raise ValueError("irad must be 0 or 1")
    return v

validate_isflux classmethod

validate_isflux(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("isflux")
@classmethod
def validate_isflux(cls, v):
    if v not in [0, 1]:
        raise ValueError("isflux must be 0 or 1")
    return v

validate_ibflux classmethod

validate_ibflux(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ibflux")
@classmethod
def validate_ibflux(cls, v):
    if v not in [0, 1]:
        raise ValueError("ibflux must be 0 or 1")
    return v

validate_iout_icm classmethod

validate_iout_icm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("iout_icm")
@classmethod
def validate_iout_icm(cls, v):
    if v not in [0, 1]:
        raise ValueError("iout_icm must be 0 or 1")
    return v

validate_nspool_icm classmethod

validate_nspool_icm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("nspool_icm")
@classmethod
def validate_nspool_icm(cls, v):
    if v <= 0:
        raise ValueError("nspool_icm must be positive")
    return v

validate_ilimit classmethod

validate_ilimit(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ilimit")
@classmethod
def validate_ilimit(cls, v):
    if v not in [0, 1]:
        raise ValueError("ilimit must be 0 or 1")
    return v

validate_idry_icm classmethod

validate_idry_icm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("idry_icm")
@classmethod
def validate_idry_icm(cls, v):
    if v not in [0, 1]:
        raise ValueError("idry_icm must be 0 or 1")
    return v

Ph_icm

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/icm.py
class Ph_icm(NamelistBaseModel):
    ppatch0: Optional[int] = Field(
        -999,
        description="Region flag for pH modeling. If set to 1, pH modeling is enabled for all elements. If set to -999, spatial pH modeling is used.",
    )
    pkcaco3: Optional[float] = Field(
        60.0,
        description="Dissolution rate constant between calcium carbonate (CaCO3) and calcium ions (Ca++)",
    )
    pkca: Optional[float] = Field(
        60.0,
        description="Sediment surface transfer coefficient from calcium carbonate (CaCO3) to calcium ions (Ca++)",
    )
    prea: Optional[float] = Field(
        1.0, description="Reaeration rate for carbon dioxide (CO2)"
    )
    inu_ph: Optional[int] = Field(
        0,
        description="Nudge option for pH model. Controls whether pH values are adjusted during simulation.",
    )

    @field_validator("ppatch0")
    @classmethod
    def validate_ppatch0(cls, v):
        if v not in [1, -999]:
            raise ValueError("ppatch0 must be either 1 or -999")
        return v

    @field_validator("pkcaco3")
    @classmethod
    def validate_pkcaco3(cls, v):
        if v <= 0:
            raise ValueError("pkcaco3 must be positive")
        return v

    @field_validator("pkca")
    @classmethod
    def validate_pkca(cls, v):
        if v <= 0:
            raise ValueError("pkca must be positive")
        return v

    @field_validator("prea")
    @classmethod
    def validate_prea(cls, v):
        if v <= 0:
            raise ValueError("prea must be positive")
        return v

    @field_validator("inu_ph")
    @classmethod
    def validate_inu_ph(cls, v):
        if v not in [0, 1]:
            raise ValueError("inu_ph must be either 0 or 1")
        return v

Attributes

ppatch0 class-attribute instance-attribute

ppatch0: Optional[int] = Field(-999, description='Region flag for pH modeling. If set to 1, pH modeling is enabled for all elements. If set to -999, spatial pH modeling is used.')

pkcaco3 class-attribute instance-attribute

pkcaco3: Optional[float] = Field(60.0, description='Dissolution rate constant between calcium carbonate (CaCO3) and calcium ions (Ca++)')

pkca class-attribute instance-attribute

pkca: Optional[float] = Field(60.0, description='Sediment surface transfer coefficient from calcium carbonate (CaCO3) to calcium ions (Ca++)')

prea class-attribute instance-attribute

prea: Optional[float] = Field(1.0, description='Reaeration rate for carbon dioxide (CO2)')

inu_ph class-attribute instance-attribute

inu_ph: Optional[int] = Field(0, description='Nudge option for pH model. Controls whether pH values are adjusted during simulation.')

Functions

validate_ppatch0 classmethod

validate_ppatch0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ppatch0")
@classmethod
def validate_ppatch0(cls, v):
    if v not in [1, -999]:
        raise ValueError("ppatch0 must be either 1 or -999")
    return v

validate_pkcaco3 classmethod

validate_pkcaco3(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("pkcaco3")
@classmethod
def validate_pkcaco3(cls, v):
    if v <= 0:
        raise ValueError("pkcaco3 must be positive")
    return v

validate_pkca classmethod

validate_pkca(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("pkca")
@classmethod
def validate_pkca(cls, v):
    if v <= 0:
        raise ValueError("pkca must be positive")
    return v

validate_prea classmethod

validate_prea(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("prea")
@classmethod
def validate_prea(cls, v):
    if v <= 0:
        raise ValueError("prea must be positive")
    return v

validate_inu_ph classmethod

validate_inu_ph(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("inu_ph")
@classmethod
def validate_inu_ph(cls, v):
    if v not in [0, 1]:
        raise ValueError("inu_ph must be either 0 or 1")
    return v

Poc

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/icm.py
class Poc(NamelistBaseModel):
    erosion: Optional[int] = Field(
        864, description="Erosion rate in kilograms per square meter per day"
    )
    etau: Optional[float] = Field(
        1e-06, description="Critical bottom shear stress in Pascals"
    )
    eporo: Optional[float] = Field(
        0.8, description="Coefficient in erosion formula (see code in icm_sfm.F90)"
    )
    efrac: Optional[float] = Field(
        0.5,
        description="Fraction coefficient in erosion formula (see code in icm_sfm.F90)",
    )
    ediso: Optional[float] = Field(
        2.5, description="H2S erosion coefficient (see code in icm_sfm.F90)"
    )
    dfrac: Optional[list] = Field(
        [0.02, 0.02],
        description="Deposition fraction of POC (Particulate Organic Carbon). If negative, dfrac will be computed",
    )
    dws_poc: Optional[list] = Field(
        [3.0, 3.0], description="Coefficient in POC erosion (see code in icm_sfm.F90)"
    )

    @field_validator("erosion")
    @classmethod
    def validate_erosion(cls, v):
        if v <= 0:
            raise ValueError("Erosion rate must be positive")
        return v

    @field_validator("etau")
    @classmethod
    def validate_etau(cls, v):
        if v <= 0:
            raise ValueError("Critical bottom shear stress must be positive")
        return v

    @field_validator("eporo")
    @classmethod
    def validate_eporo(cls, v):
        if not 0 <= v <= 1:
            raise ValueError("Eporo must be between 0 and 1")
        return v

    @field_validator("efrac")
    @classmethod
    def validate_efrac(cls, v):
        if not 0 <= v <= 1:
            raise ValueError("Efrac must be between 0 and 1")
        return v

    @field_validator("ediso")
    @classmethod
    def validate_ediso(cls, v):
        if v <= 0:
            raise ValueError("Ediso must be positive")
        return v

    @field_validator("dfrac")
    @classmethod
    def validate_dfrac(cls, v):
        if not (isinstance(v, list) and len(v) == 2):
            raise ValueError("Dfrac must be a list of two float values")
        return v

    @field_validator("dws_poc")
    @classmethod
    def validate_dws_poc(cls, v):
        if not (isinstance(v, list) and len(v) == 2 and all(x > 0 for x in v)):
            raise ValueError("DWS_POC must be a list of two positive float values")
        return v

Attributes

erosion class-attribute instance-attribute

erosion: Optional[int] = Field(864, description='Erosion rate in kilograms per square meter per day')

etau class-attribute instance-attribute

etau: Optional[float] = Field(1e-06, description='Critical bottom shear stress in Pascals')

eporo class-attribute instance-attribute

eporo: Optional[float] = Field(0.8, description='Coefficient in erosion formula (see code in icm_sfm.F90)')

efrac class-attribute instance-attribute

efrac: Optional[float] = Field(0.5, description='Fraction coefficient in erosion formula (see code in icm_sfm.F90)')

ediso class-attribute instance-attribute

ediso: Optional[float] = Field(2.5, description='H2S erosion coefficient (see code in icm_sfm.F90)')

dfrac class-attribute instance-attribute

dfrac: Optional[list] = Field([0.02, 0.02], description='Deposition fraction of POC (Particulate Organic Carbon). If negative, dfrac will be computed')

dws_poc class-attribute instance-attribute

dws_poc: Optional[list] = Field([3.0, 3.0], description='Coefficient in POC erosion (see code in icm_sfm.F90)')

Functions

validate_erosion classmethod

validate_erosion(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("erosion")
@classmethod
def validate_erosion(cls, v):
    if v <= 0:
        raise ValueError("Erosion rate must be positive")
    return v

validate_etau classmethod

validate_etau(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("etau")
@classmethod
def validate_etau(cls, v):
    if v <= 0:
        raise ValueError("Critical bottom shear stress must be positive")
    return v

validate_eporo classmethod

validate_eporo(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("eporo")
@classmethod
def validate_eporo(cls, v):
    if not 0 <= v <= 1:
        raise ValueError("Eporo must be between 0 and 1")
    return v

validate_efrac classmethod

validate_efrac(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("efrac")
@classmethod
def validate_efrac(cls, v):
    if not 0 <= v <= 1:
        raise ValueError("Efrac must be between 0 and 1")
    return v

validate_ediso classmethod

validate_ediso(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ediso")
@classmethod
def validate_ediso(cls, v):
    if v <= 0:
        raise ValueError("Ediso must be positive")
    return v

validate_dfrac classmethod

validate_dfrac(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("dfrac")
@classmethod
def validate_dfrac(cls, v):
    if not (isinstance(v, list) and len(v) == 2):
        raise ValueError("Dfrac must be a list of two float values")
    return v

validate_dws_poc classmethod

validate_dws_poc(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("dws_poc")
@classmethod
def validate_dws_poc(cls, v):
    if not (isinstance(v, list) and len(v) == 2 and all(x > 0 for x in v)):
        raise ValueError("DWS_POC must be a list of two positive float values")
    return v

Sav

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/icm.py
class Sav(NamelistBaseModel):
    spatch0: Optional[int] = Field(
        -999,
        description="Region flag for SAV. 1 indicates SAV is active on all elements, -999 indicates spatial distribution",
    )
    stleaf0: Optional[int] = Field(
        -999, description="Initial concentration of total SAV leaf biomass"
    )
    ststem0: Optional[int] = Field(
        -999, description="Initial concentration of total SAV stem biomass"
    )
    stroot0: Optional[int] = Field(
        -999, description="Initial concentration of total SAV root biomass"
    )
    sgpm: Optional[float] = Field(0.1, description="Maximum growth rate of SAV per day")
    stgp: Optional[int] = Field(
        32, description="Optimal growth temperature for SAV in degrees Celsius"
    )
    sktgp: Optional[list] = Field(
        [0.003, 0.005],
        description="Temperature dependence coefficients for SAV growth (for T<=sTGP and T>sTGP)",
    )
    sfam: Optional[float] = Field(
        0.2,
        description="Fraction of SAV leaf production allocated to active metabolism",
    )
    sfcp: Optional[list] = Field(
        [0.6, 0.3, 0.1],
        description="Fractions of SAV production allocated to leaf, stem, and root biomass",
    )
    smtb: Optional[list] = Field(
        [0.02, 0.02, 0.02], description="Metabolism rates of SAV leaf, stem, and root"
    )
    stmt: Optional[list] = Field(
        [20, 20, 20],
        description="Reference temperatures for SAV leaf, stem, and root metabolism in degrees Celsius",
    )
    sktmt: Optional[list] = Field(
        [0.069, 0.069, 0.069],
        description="Temperature dependence coefficients for SAV leaf, stem, and root metabolism",
    )
    sfcm: Optional[list] = Field(
        [0.05, 0.15, 0.3, 0.5],
        description="Fractions of SAV metabolism carbon allocated to RPOC, LPOC, DOC, and CO2",
    )
    sfnm: Optional[list] = Field(
        [0.05, 0.15, 0.3, 0.5],
        description="Fractions of SAV metabolism nitrogen allocated to RPON, LPON, DON, and NH4",
    )
    sfpm: Optional[list] = Field(
        [0.05, 0.1, 0.35, 0.5],
        description="Fractions of SAV metabolism phosphorus allocated to RPOP, LPOP, DOP, and PO4",
    )
    skhnw: Optional[float] = Field(
        0.01, description="Nitrogen half-saturation constant for SAV in water column"
    )
    skhns: Optional[float] = Field(
        0.1, description="Nitrogen half-saturation constant for SAV in sediments"
    )
    skhnh4: Optional[float] = Field(
        0.1, description="Ammonium half-saturation constant for SAV"
    )
    skhpw: Optional[float] = Field(
        0.001, description="Phosphorus half-saturation constant for SAV in water column"
    )
    skhps: Optional[float] = Field(
        0.01, description="Phosphorus half-saturation constant for SAV in sediments"
    )
    salpha: Optional[float] = Field(
        0.006, description="Initial slope of the SAV photosynthesis-irradiance curve"
    )
    ske: Optional[float] = Field(
        0.045, description="Light attenuation coefficient due to SAV absorption"
    )
    shtm: Optional[list] = Field(
        [0.054, 2.0], description="Minimum (base) and maximum SAV canopy height"
    )
    s2ht: Optional[list] = Field(
        [0.0036, 0.0036, 0.0],
        description="Coefficients for converting SAV leaf, stem, and root biomass to canopy height",
    )
    sc2dw: Optional[float] = Field(
        0.38, description="Carbon to dry weight ratio of SAV"
    )
    s2den: Optional[int] = Field(
        10, description="Coefficient for computing SAV density from leaf biomass"
    )

    @field_validator("spatch0")
    @classmethod
    def validate_spatch0(cls, v):
        if v not in [1, -999]:
            raise ValueError("spatch0 must be either 1 or -999")
        return v

    @field_validator("stleaf0")
    @classmethod
    def validate_stleaf0(cls, v):
        if v < 0 and v != -999:
            raise ValueError("stleaf0 must be non-negative or -999")
        return v

    @field_validator("ststem0")
    @classmethod
    def validate_ststem0(cls, v):
        if v < 0 and v != -999:
            raise ValueError("ststem0 must be non-negative or -999")
        return v

    @field_validator("stroot0")
    @classmethod
    def validate_stroot0(cls, v):
        if v < 0 and v != -999:
            raise ValueError("stroot0 must be non-negative or -999")
        return v

    @field_validator("sgpm")
    @classmethod
    def validate_sgpm(cls, v):
        if v <= 0 or v > 1:
            raise ValueError("sgpm must be between 0 and 1")
        return v

    @field_validator("stgp")
    @classmethod
    def validate_stgp(cls, v):
        if v < 0 or v > 50:
            raise ValueError("stgp must be between 0 and 50")
        return v

    @field_validator("sktgp")
    @classmethod
    def validate_sktgp(cls, v):
        if len(v) != 2 or any(x <= 0 for x in v):
            raise ValueError("sktgp must be a list of two positive numbers")
        return v

    @field_validator("sfam")
    @classmethod
    def validate_sfam(cls, v):
        if v < 0 or v > 1:
            raise ValueError("sfam must be between 0 and 1")
        return v

    @field_validator("sfcp")
    @classmethod
    def validate_sfcp(cls, v):
        if len(v) != 3 or sum(v) != 1 or any(x < 0 for x in v):
            raise ValueError(
                "sfcp must be a list of three non-negative numbers that sum to 1"
            )
        return v

    @field_validator("smtb")
    @classmethod
    def validate_smtb(cls, v):
        if len(v) != 3 or any(x <= 0 for x in v):
            raise ValueError("smtb must be a list of three positive numbers")
        return v

    @field_validator("stmt")
    @classmethod
    def validate_stmt(cls, v):
        if len(v) != 3 or any(x < 0 or x > 50 for x in v):
            raise ValueError("stmt must be a list of three numbers between 0 and 50")
        return v

    @field_validator("sktmt")
    @classmethod
    def validate_sktmt(cls, v):
        if len(v) != 3 or any(x <= 0 for x in v):
            raise ValueError("sktmt must be a list of three positive numbers")
        return v

    @field_validator("sfcm")
    @classmethod
    def validate_sfcm(cls, v):
        if len(v) != 4 or sum(v) != 1 or any(x < 0 for x in v):
            raise ValueError(
                "sfcm must be a list of four non-negative numbers that sum to 1"
            )
        return v

    @field_validator("sfnm")
    @classmethod
    def validate_sfnm(cls, v):
        if len(v) != 4 or sum(v) != 1 or any(x < 0 for x in v):
            raise ValueError(
                "sfnm must be a list of four non-negative numbers that sum to 1"
            )
        return v

    @field_validator("sfpm")
    @classmethod
    def validate_sfpm(cls, v):
        if len(v) != 4 or sum(v) != 1 or any(x < 0 for x in v):
            raise ValueError(
                "sfpm must be a list of four non-negative numbers that sum to 1"
            )
        return v

    @field_validator("skhnw")
    @classmethod
    def validate_skhnw(cls, v):
        if v <= 0:
            raise ValueError("skhnw must be positive")
        return v

    @field_validator("skhns")
    @classmethod
    def validate_skhns(cls, v):
        if v <= 0:
            raise ValueError("skhns must be positive")
        return v

    @field_validator("skhnh4")
    @classmethod
    def validate_skhnh4(cls, v):
        if v <= 0:
            raise ValueError("skhnh4 must be positive")
        return v

    @field_validator("skhpw")
    @classmethod
    def validate_skhpw(cls, v):
        if v <= 0:
            raise ValueError("skhpw must be positive")
        return v

    @field_validator("skhps")
    @classmethod
    def validate_skhps(cls, v):
        if v <= 0:
            raise ValueError("skhps must be positive")
        return v

    @field_validator("salpha")
    @classmethod
    def validate_salpha(cls, v):
        if v <= 0:
            raise ValueError("salpha must be positive")
        return v

    @field_validator("ske")
    @classmethod
    def validate_ske(cls, v):
        if v <= 0:
            raise ValueError("ske must be positive")
        return v

    @field_validator("shtm")
    @classmethod
    def validate_shtm(cls, v):
        if len(v) != 2 or v[0] >= v[1] or any(x <= 0 for x in v):
            raise ValueError(
                "shtm must be a list of two positive numbers with the first less than the second"
            )
        return v

    @field_validator("s2ht")
    @classmethod
    def validate_s2ht(cls, v):
        if len(v) != 3 or any(x < 0 for x in v):
            raise ValueError("s2ht must be a list of three non-negative numbers")
        return v

    @field_validator("sc2dw")
    @classmethod
    def validate_sc2dw(cls, v):
        if v <= 0 or v > 1:
            raise ValueError("sc2dw must be between 0 and 1")
        return v

    @field_validator("s2den")
    @classmethod
    def validate_s2den(cls, v):
        if v <= 0:
            raise ValueError("s2den must be positive")
        return v

Attributes

spatch0 class-attribute instance-attribute

spatch0: Optional[int] = Field(-999, description='Region flag for SAV. 1 indicates SAV is active on all elements, -999 indicates spatial distribution')

stleaf0 class-attribute instance-attribute

stleaf0: Optional[int] = Field(-999, description='Initial concentration of total SAV leaf biomass')

ststem0 class-attribute instance-attribute

ststem0: Optional[int] = Field(-999, description='Initial concentration of total SAV stem biomass')

stroot0 class-attribute instance-attribute

stroot0: Optional[int] = Field(-999, description='Initial concentration of total SAV root biomass')

sgpm class-attribute instance-attribute

sgpm: Optional[float] = Field(0.1, description='Maximum growth rate of SAV per day')

stgp class-attribute instance-attribute

stgp: Optional[int] = Field(32, description='Optimal growth temperature for SAV in degrees Celsius')

sktgp class-attribute instance-attribute

sktgp: Optional[list] = Field([0.003, 0.005], description='Temperature dependence coefficients for SAV growth (for T<=sTGP and T>sTGP)')

sfam class-attribute instance-attribute

sfam: Optional[float] = Field(0.2, description='Fraction of SAV leaf production allocated to active metabolism')

sfcp class-attribute instance-attribute

sfcp: Optional[list] = Field([0.6, 0.3, 0.1], description='Fractions of SAV production allocated to leaf, stem, and root biomass')

smtb class-attribute instance-attribute

smtb: Optional[list] = Field([0.02, 0.02, 0.02], description='Metabolism rates of SAV leaf, stem, and root')

stmt class-attribute instance-attribute

stmt: Optional[list] = Field([20, 20, 20], description='Reference temperatures for SAV leaf, stem, and root metabolism in degrees Celsius')

sktmt class-attribute instance-attribute

sktmt: Optional[list] = Field([0.069, 0.069, 0.069], description='Temperature dependence coefficients for SAV leaf, stem, and root metabolism')

sfcm class-attribute instance-attribute

sfcm: Optional[list] = Field([0.05, 0.15, 0.3, 0.5], description='Fractions of SAV metabolism carbon allocated to RPOC, LPOC, DOC, and CO2')

sfnm class-attribute instance-attribute

sfnm: Optional[list] = Field([0.05, 0.15, 0.3, 0.5], description='Fractions of SAV metabolism nitrogen allocated to RPON, LPON, DON, and NH4')

sfpm class-attribute instance-attribute

sfpm: Optional[list] = Field([0.05, 0.1, 0.35, 0.5], description='Fractions of SAV metabolism phosphorus allocated to RPOP, LPOP, DOP, and PO4')

skhnw class-attribute instance-attribute

skhnw: Optional[float] = Field(0.01, description='Nitrogen half-saturation constant for SAV in water column')

skhns class-attribute instance-attribute

skhns: Optional[float] = Field(0.1, description='Nitrogen half-saturation constant for SAV in sediments')

skhnh4 class-attribute instance-attribute

skhnh4: Optional[float] = Field(0.1, description='Ammonium half-saturation constant for SAV')

skhpw class-attribute instance-attribute

skhpw: Optional[float] = Field(0.001, description='Phosphorus half-saturation constant for SAV in water column')

skhps class-attribute instance-attribute

skhps: Optional[float] = Field(0.01, description='Phosphorus half-saturation constant for SAV in sediments')

salpha class-attribute instance-attribute

salpha: Optional[float] = Field(0.006, description='Initial slope of the SAV photosynthesis-irradiance curve')

ske class-attribute instance-attribute

ske: Optional[float] = Field(0.045, description='Light attenuation coefficient due to SAV absorption')

shtm class-attribute instance-attribute

shtm: Optional[list] = Field([0.054, 2.0], description='Minimum (base) and maximum SAV canopy height')

s2ht class-attribute instance-attribute

s2ht: Optional[list] = Field([0.0036, 0.0036, 0.0], description='Coefficients for converting SAV leaf, stem, and root biomass to canopy height')

sc2dw class-attribute instance-attribute

sc2dw: Optional[float] = Field(0.38, description='Carbon to dry weight ratio of SAV')

s2den class-attribute instance-attribute

s2den: Optional[int] = Field(10, description='Coefficient for computing SAV density from leaf biomass')

Functions

validate_spatch0 classmethod

validate_spatch0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("spatch0")
@classmethod
def validate_spatch0(cls, v):
    if v not in [1, -999]:
        raise ValueError("spatch0 must be either 1 or -999")
    return v

validate_stleaf0 classmethod

validate_stleaf0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("stleaf0")
@classmethod
def validate_stleaf0(cls, v):
    if v < 0 and v != -999:
        raise ValueError("stleaf0 must be non-negative or -999")
    return v

validate_ststem0 classmethod

validate_ststem0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ststem0")
@classmethod
def validate_ststem0(cls, v):
    if v < 0 and v != -999:
        raise ValueError("ststem0 must be non-negative or -999")
    return v

validate_stroot0 classmethod

validate_stroot0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("stroot0")
@classmethod
def validate_stroot0(cls, v):
    if v < 0 and v != -999:
        raise ValueError("stroot0 must be non-negative or -999")
    return v

validate_sgpm classmethod

validate_sgpm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("sgpm")
@classmethod
def validate_sgpm(cls, v):
    if v <= 0 or v > 1:
        raise ValueError("sgpm must be between 0 and 1")
    return v

validate_stgp classmethod

validate_stgp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("stgp")
@classmethod
def validate_stgp(cls, v):
    if v < 0 or v > 50:
        raise ValueError("stgp must be between 0 and 50")
    return v

validate_sktgp classmethod

validate_sktgp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("sktgp")
@classmethod
def validate_sktgp(cls, v):
    if len(v) != 2 or any(x <= 0 for x in v):
        raise ValueError("sktgp must be a list of two positive numbers")
    return v

validate_sfam classmethod

validate_sfam(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("sfam")
@classmethod
def validate_sfam(cls, v):
    if v < 0 or v > 1:
        raise ValueError("sfam must be between 0 and 1")
    return v

validate_sfcp classmethod

validate_sfcp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("sfcp")
@classmethod
def validate_sfcp(cls, v):
    if len(v) != 3 or sum(v) != 1 or any(x < 0 for x in v):
        raise ValueError(
            "sfcp must be a list of three non-negative numbers that sum to 1"
        )
    return v

validate_smtb classmethod

validate_smtb(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("smtb")
@classmethod
def validate_smtb(cls, v):
    if len(v) != 3 or any(x <= 0 for x in v):
        raise ValueError("smtb must be a list of three positive numbers")
    return v

validate_stmt classmethod

validate_stmt(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("stmt")
@classmethod
def validate_stmt(cls, v):
    if len(v) != 3 or any(x < 0 or x > 50 for x in v):
        raise ValueError("stmt must be a list of three numbers between 0 and 50")
    return v

validate_sktmt classmethod

validate_sktmt(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("sktmt")
@classmethod
def validate_sktmt(cls, v):
    if len(v) != 3 or any(x <= 0 for x in v):
        raise ValueError("sktmt must be a list of three positive numbers")
    return v

validate_sfcm classmethod

validate_sfcm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("sfcm")
@classmethod
def validate_sfcm(cls, v):
    if len(v) != 4 or sum(v) != 1 or any(x < 0 for x in v):
        raise ValueError(
            "sfcm must be a list of four non-negative numbers that sum to 1"
        )
    return v

validate_sfnm classmethod

validate_sfnm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("sfnm")
@classmethod
def validate_sfnm(cls, v):
    if len(v) != 4 or sum(v) != 1 or any(x < 0 for x in v):
        raise ValueError(
            "sfnm must be a list of four non-negative numbers that sum to 1"
        )
    return v

validate_sfpm classmethod

validate_sfpm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("sfpm")
@classmethod
def validate_sfpm(cls, v):
    if len(v) != 4 or sum(v) != 1 or any(x < 0 for x in v):
        raise ValueError(
            "sfpm must be a list of four non-negative numbers that sum to 1"
        )
    return v

validate_skhnw classmethod

validate_skhnw(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("skhnw")
@classmethod
def validate_skhnw(cls, v):
    if v <= 0:
        raise ValueError("skhnw must be positive")
    return v

validate_skhns classmethod

validate_skhns(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("skhns")
@classmethod
def validate_skhns(cls, v):
    if v <= 0:
        raise ValueError("skhns must be positive")
    return v

validate_skhnh4 classmethod

validate_skhnh4(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("skhnh4")
@classmethod
def validate_skhnh4(cls, v):
    if v <= 0:
        raise ValueError("skhnh4 must be positive")
    return v

validate_skhpw classmethod

validate_skhpw(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("skhpw")
@classmethod
def validate_skhpw(cls, v):
    if v <= 0:
        raise ValueError("skhpw must be positive")
    return v

validate_skhps classmethod

validate_skhps(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("skhps")
@classmethod
def validate_skhps(cls, v):
    if v <= 0:
        raise ValueError("skhps must be positive")
    return v

validate_salpha classmethod

validate_salpha(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("salpha")
@classmethod
def validate_salpha(cls, v):
    if v <= 0:
        raise ValueError("salpha must be positive")
    return v

validate_ske classmethod

validate_ske(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ske")
@classmethod
def validate_ske(cls, v):
    if v <= 0:
        raise ValueError("ske must be positive")
    return v

validate_shtm classmethod

validate_shtm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("shtm")
@classmethod
def validate_shtm(cls, v):
    if len(v) != 2 or v[0] >= v[1] or any(x <= 0 for x in v):
        raise ValueError(
            "shtm must be a list of two positive numbers with the first less than the second"
        )
    return v

validate_s2ht classmethod

validate_s2ht(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("s2ht")
@classmethod
def validate_s2ht(cls, v):
    if len(v) != 3 or any(x < 0 for x in v):
        raise ValueError("s2ht must be a list of three non-negative numbers")
    return v

validate_sc2dw classmethod

validate_sc2dw(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("sc2dw")
@classmethod
def validate_sc2dw(cls, v):
    if v <= 0 or v > 1:
        raise ValueError("sc2dw must be between 0 and 1")
    return v

validate_s2den classmethod

validate_s2den(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("s2den")
@classmethod
def validate_s2den(cls, v):
    if v <= 0:
        raise ValueError("s2den must be positive")
    return v

Sfm

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/icm.py
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
class Sfm(NamelistBaseModel):
    btemp0: Optional[float] = Field(
        5.0, description="Initial temperature of the sediment layer in degrees Celsius"
    )
    bstc0: Optional[float] = Field(
        0.1, description="Initial surface transfer coefficient for the sediment layer"
    )
    bstr0: Optional[float] = Field(0.0, description="Initial benthic stress in days")
    bthp0: Optional[float] = Field(
        0.0, description="Initial consecutive days of hypoxia"
    )
    btox0: Optional[float] = Field(
        0.0,
        description="Initial consecutive days of oxic condition after hypoxia event",
    )
    bnh40: Optional[float] = Field(
        4.0, description="Initial NH4 concentration in the sediment layer (g/m3)"
    )
    bno30: Optional[float] = Field(
        1.0, description="Initial NO3 concentration in the sediment layer (g/m3)"
    )
    bpo40: Optional[float] = Field(
        5.0, description="Initial PO4 concentration in the sediment layer (g/m3)"
    )
    bh2s0: Optional[float] = Field(
        250.0, description="Initial H2S concentration in the sediment layer (g/m3)"
    )
    bch40: Optional[float] = Field(
        40.0, description="Initial CH4 concentration in the sediment layer (g/m3)"
    )
    bpos0: Optional[float] = Field(
        500.0,
        description="Initial POS (Particulate Organic Silica) concentration in the sediment layer (g/m3)",
    )
    bsa0: Optional[float] = Field(
        500.0,
        description="Initial SA (Salinity) concentration in the sediment layer (g/m3)",
    )
    bpoc0: Optional[list] = Field(
        [1000.0, 3000.0, 5000.0],
        description="Initial POC (Particulate Organic Carbon) concentrations for 3 classes (G1, G2, G3) in the sediment layer (g/m3)",
    )
    bpon0: Optional[list] = Field(
        [150.0, 500.0, 1500.0],
        description="Initial PON (Particulate Organic Nitrogen) concentrations for 3 classes (G1, G2, G3) in the sediment layer (g/m3)",
    )
    bpop0: Optional[list] = Field(
        [30.0, 300.0, 500.0],
        description="Initial POP (Particulate Organic Phosphorus) concentrations for 3 classes (G1, G2, G3) in the sediment layer (g/m3)",
    )
    bdz: Optional[float] = Field(0.1, description="Sediment thickness (m)")
    bvb: Optional[float] = Field(1.37e-05, description="Burial rate (m/day)")
    bsolid: Optional[list] = Field(
        [0.5, 0.5],
        description="Sediment solid concentrations in Layer 1 and Layer 2 (Kg/L)",
    )
    bdiff: Optional[float] = Field(
        1.8e-07, description="Diffusion coefficient for sediment temperature (m2/s)"
    )
    btr: Optional[int] = Field(
        20, description="Reference temperature for sediment processes (°C)"
    )
    bvpmin: Optional[float] = Field(
        3e-06, description="minimum particle mixing velocity coefficient (m.day-1)"
    )
    bvp: Optional[float] = Field(
        0.00012, description="particle mixing velocity coefficient (m.day-1)"
    )
    bvd: Optional[float] = Field(
        0.001, description="diffusion velocity coefficient (m.day-1)"
    )
    bktvp: Optional[float] = Field(
        1.117, description="temp. dependece of particle mixing velocity"
    )
    bktvd: Optional[float] = Field(
        1.08, description="temp. dependece of diffusion velocity"
    )
    bkst: Optional[float] = Field(
        0.03, description="1st order decay rate of benthic stress  (day-1)"
    )
    bstmax: Optional[float] = Field(
        20.0,
        description="maximum value of benthic stress (day) (note: smaller than 1/bKST)",
    )
    bkhdo_vp: Optional[float] = Field(
        4.0, description="DO half-saturation of particle mixing (mg/L)"
    )
    bdoc_st: Optional[float] = Field(
        1.0, description="DO criteria for benthic stress (mg/L)"
    )
    banoxic: Optional[float] = Field(
        10.0,
        description="consective days of hypoxia causing maximum benthic stress (day)",
    )
    boxic: Optional[float] = Field(
        45.0, description="time lag for bethos recovery from hypoxia event (day)"
    )
    bp2d: Optional[float] = Field(
        0.0,
        description="ratio from mixing coef. to diffusion coef. (benthos enhanced effect)",
    )
    bkc: Optional[list] = Field(
        [0.035, 0.0018, 0.0], description="decay rate of POC (3G class) at bTR (day-1)"
    )
    bkn: Optional[list] = Field(
        [0.035, 0.0018, 0.0], description="decay rate of PON (3G class) at bTR (day-1)"
    )
    bkp: Optional[list] = Field(
        [0.035, 0.0018, 0.0], description="decay rate of POP (3G class) at bTR (day-1)"
    )
    bktc: Optional[list] = Field(
        [1.1, 1.15, 1.17], description="temp. dependence of POC decay (oC-1)"
    )
    bktn: Optional[list] = Field(
        [1.1, 1.15, 1.17], description="temp. dependence of PON decay (oC-1)"
    )
    bktp: Optional[list] = Field(
        [1.1, 1.15, 1.17], description="temp. dependence of POP decay (oC-1)"
    )
    bfcp: Optional[list] = Field(
        [0.35, 0.55, 0.01, 0.35, 0.55, 0.01, 0.35, 0.55, 0.01],
        description="Phyto POC into sed POC (G3,PB=1:3)",
    )
    bfnp: Optional[list] = Field(
        [0.35, 0.55, 0.01, 0.35, 0.55, 0.01, 0.35, 0.55, 0.01],
        description="Phyto PON into sed PON (G3,PB=1:3)",
    )
    bfpp: Optional[list] = Field(
        [0.35, 0.55, 0.01, 0.35, 0.55, 0.01, 0.35, 0.55, 0.01],
        description="Phyto POP into sed POP (G3,PB=1:3)",
    )
    bfcm: Optional[list] = Field(
        [0.0, 0.43, 0.57], description="refractory POC into sed POC(3G)"
    )
    bfnm: Optional[list] = Field(
        [0.0, 0.54, 0.46], description="refractory PON into sed PON(3G)"
    )
    bfpm: Optional[list] = Field(
        [0.0, 0.43, 0.57], description="refractory POP into sed POP(3G)"
    )
    bknh4f: Optional[float] = Field(
        0.2, description="NH4 reaction rate in freshwater  at bTR (1st layer) (m/day)"
    )
    bknh4s: Optional[float] = Field(
        0.14, description="NH4 reaction rate in salty water at bTR (1st layer) (m/day)"
    )
    bktnh4: Optional[float] = Field(
        1.08, description="temp. dependency for NH4 reaction (oC-1)"
    )
    bkhnh4: Optional[float] = Field(
        1.5, description="half-stauration NH4 for nitrification (g/m3)"
    )
    bkhdo_nh4: Optional[float] = Field(
        2.0, description="half-stauration DO for nitrification (g/m3)"
    )
    bpienh4: Optional[float] = Field(
        1.0, description="partition coefficients of NH4 in Layer 1 & 2 (Kg-1.L)"
    )
    bsaltn: Optional[float] = Field(
        1.0,
        description="salinity criteria of fresh/salty water for NH4/NO3 reaction (PSU)",
    )
    bkno3f: Optional[float] = Field(
        0.3, description="NO3 reaction rate in freshwater at bTR (1st layer) (m/day)"
    )
    bkno3s: Optional[float] = Field(
        0.125, description="NO3 reaction rate in salty water at bTR (1st layer) (m/day)"
    )
    bkno3: Optional[float] = Field(
        0.25, description="NO3 reaction rate (2nd layer) (m/day)"
    )
    bktno3: Optional[float] = Field(
        1.08, description="temp. dependency for NO3 reaction (oC-1)"
    )
    bkh2sd: Optional[float] = Field(
        0.2, description="dissolved H2S reaction rate at bTR (1st layer) (m/day)"
    )
    bkh2sp: Optional[float] = Field(
        0.4, description="particulate H2S reaction rate at bTR (1st layer) (m/day)"
    )
    bkth2s: Optional[float] = Field(
        1.08, description="temp. dependency for H2S reaction (oC-1)"
    )
    bpieh2ss: Optional[float] = Field(
        100.0, description="partition coefficient of NH4 in Layer 1 (Kg-1.L)"
    )
    bpieh2sb: Optional[float] = Field(
        100.0, description="partition coefficient of NH4 in Layer 2 (Kg-1.L)"
    )
    bkhdo_h2s: Optional[float] = Field(
        8.0, description="O2 constant to normalize H2S oxidation (g[O2]/m3)"
    )
    bsaltc: Optional[float] = Field(
        1.0,
        description="salinity criteria of fresh/salty water for carbon reaction (PSU)",
    )
    bkch4: Optional[float] = Field(
        0.2, description="CH4 reaction rate at bTR (1st layer) (m/day)"
    )
    bktch4: Optional[float] = Field(
        1.08, description="temp. dependency for CH4 reaction"
    )
    bkhdo_ch4: Optional[float] = Field(
        0.2, description="half-saturation DO for CH4 oxidation (g[O2]/m3)"
    )
    bo2n: Optional[float] = Field(
        2.86, description="oxygen to nitrogen ratio in sediment (denitrification)"
    )
    bpiepo4: Optional[float] = Field(
        50.0, description="partition coefficient of PO4 in Layer 2 (Kg-1.L)"
    )
    bkopo4f: Optional[float] = Field(
        3000.0,
        description="oxygen dependency for PO4 sorption in freshwater in Layer 1",
    )
    bkopo4s: Optional[float] = Field(
        300.0,
        description="oxygen dependency for PO4 sorption in salty water in Layer 1",
    )
    bdoc_po4: Optional[float] = Field(
        1.0, description="DO criteria for PO4 sorptiona (g[O2]/m3)"
    )
    bsaltp: Optional[float] = Field(
        1.0,
        description="salinity criteria of fresh/salty water for PO4 partition (PSU)",
    )
    bks: Optional[float] = Field(0.5, description="decay rate of POS (3G class) at bTR")
    bkts: Optional[float] = Field(1.1, description="temp. dependence of POS decay")
    bsisat: Optional[float] = Field(
        40.0, description="silica saturation conc. in pore water (g[Si]/m3)"
    )
    bpiesi: Optional[float] = Field(
        100.0, description="partition coefficient of silica in Layer 2 (Kg-1.L)"
    )
    bkosi: Optional[float] = Field(
        10.0, description="oxygen dependency for silica sorption in Layer 1"
    )
    bkhpos: Optional[float] = Field(
        50000.0, description="POS half saturation for POS dissolution (g/m3)"
    )
    bdoc_si: Optional[float] = Field(
        1.0, description="DO criteria for silica sorptiona (g[O2]/m3)"
    )
    bjposa: Optional[float] = Field(
        0.0,
        description="additional POS flux associated with POM detrius beside algea (g.m-2.day-1)",
    )
    bfcs: Optional[list] = Field(
        [0.65, 0.255, 0.095], description="SAV POC into 3G sed 3G POC"
    )
    bfns: Optional[list] = Field(
        [0.65, 0.3, 0.05], description="SAV PON into 3G sed 3G PON"
    )
    bfps: Optional[list] = Field(
        [0.65, 0.255, 0.095], description="SAV POP into 3G sed 3G POP"
    )
    bfcv: Optional[list] = Field(
        [0.65, 0.255, 0.095, 0.65, 0.255, 0.095, 0.65, 0.255, 0.095],
        description="VEG POC into sed POC (G3,PB=1:3)",
    )
    bfnv: Optional[list] = Field(
        [0.65, 0.3, 0.05, 0.65, 0.3, 0.05, 0.65, 0.3, 0.05],
        description="VEG PON into sed PON (G3,PB=1:3)",
    )
    bfpv: Optional[list] = Field(
        [0.65, 0.255, 0.095, 0.65, 0.255, 0.095, 0.65, 0.255, 0.095],
        description="VEG POP into sed POP (G3,PB=1:3)",
    )

    @field_validator("btemp0")
    @classmethod
    def validate_btemp0(cls, v):
        if v < -2 or v > 40:
            raise ValueError("btemp0 must be between -2 and 40")
        return v

    @field_validator("bstc0")
    @classmethod
    def validate_bstc0(cls, v):
        if v < 0:
            raise ValueError("bstc0 must be non-negative")
        return v

    @field_validator("bstr0")
    @classmethod
    def validate_bstr0(cls, v):
        if v < 0:
            raise ValueError("bstr0 must be non-negative")
        return v

    @field_validator("bthp0")
    @classmethod
    def validate_bthp0(cls, v):
        if v < 0:
            raise ValueError("bthp0 must be non-negative")
        return v

    @field_validator("btox0")
    @classmethod
    def validate_btox0(cls, v):
        if v < 0:
            raise ValueError("btox0 must be non-negative")
        return v

    @field_validator("bnh40")
    @classmethod
    def validate_bnh40(cls, v):
        if v < 0:
            raise ValueError("bnh40 must be non-negative")
        return v

    @field_validator("bno30")
    @classmethod
    def validate_bno30(cls, v):
        if v < 0:
            raise ValueError("bno30 must be non-negative")
        return v

    @field_validator("bpo40")
    @classmethod
    def validate_bpo40(cls, v):
        if v < 0:
            raise ValueError("bpo40 must be non-negative")
        return v

    @field_validator("bh2s0")
    @classmethod
    def validate_bh2s0(cls, v):
        if v < 0:
            raise ValueError("bh2s0 must be non-negative")
        return v

    @field_validator("bch40")
    @classmethod
    def validate_bch40(cls, v):
        if v < 0:
            raise ValueError("bch40 must be non-negative")
        return v

    @field_validator("bpos0")
    @classmethod
    def validate_bpos0(cls, v):
        if v < 0:
            raise ValueError("bpos0 must be non-negative")
        return v

    @field_validator("bsa0")
    @classmethod
    def validate_bsa0(cls, v):
        if v < 0:
            raise ValueError("bsa0 must be non-negative")
        return v

    @field_validator("bpoc0")
    @classmethod
    def validate_bpoc0(cls, v):
        if len(v) != 3 or any(x < 0 for x in v):
            raise ValueError("bpoc0 must be a list of 3 non-negative values")
        return v

    @field_validator("bpon0")
    @classmethod
    def validate_bpon0(cls, v):
        if len(v) != 3 or any(x < 0 for x in v):
            raise ValueError("bpon0 must be a list of 3 non-negative values")
        return v

    @field_validator("bpop0")
    @classmethod
    def validate_bpop0(cls, v):
        if len(v) != 3 or any(x < 0 for x in v):
            raise ValueError("bpop0 must be a list of 3 non-negative values")
        return v

    @field_validator("bdz")
    @classmethod
    def validate_bdz(cls, v):
        if v <= 0:
            raise ValueError("bdz must be positive")
        return v

    @field_validator("bvb")
    @classmethod
    def validate_bvb(cls, v):
        if v < 0:
            raise ValueError("bvb must be non-negative")
        return v

    @field_validator("bsolid")
    @classmethod
    def validate_bsolid(cls, v):
        if len(v) != 2 or any(x <= 0 or x > 1 for x in v):
            raise ValueError("bsolid must be a list of 2 values between 0 and 1")
        return v

    @field_validator("bdiff")
    @classmethod
    def validate_bdiff(cls, v):
        if v <= 0:
            raise ValueError("bdiff must be positive")
        return v

    @field_validator("btr")
    @classmethod
    def validate_btr(cls, v):
        if v < 0 or v > 40:
            raise ValueError("btr must be between 0 and 40")
        return v

Attributes

btemp0 class-attribute instance-attribute

btemp0: Optional[float] = Field(5.0, description='Initial temperature of the sediment layer in degrees Celsius')

bstc0 class-attribute instance-attribute

bstc0: Optional[float] = Field(0.1, description='Initial surface transfer coefficient for the sediment layer')

bstr0 class-attribute instance-attribute

bstr0: Optional[float] = Field(0.0, description='Initial benthic stress in days')

bthp0 class-attribute instance-attribute

bthp0: Optional[float] = Field(0.0, description='Initial consecutive days of hypoxia')

btox0 class-attribute instance-attribute

btox0: Optional[float] = Field(0.0, description='Initial consecutive days of oxic condition after hypoxia event')

bnh40 class-attribute instance-attribute

bnh40: Optional[float] = Field(4.0, description='Initial NH4 concentration in the sediment layer (g/m3)')

bno30 class-attribute instance-attribute

bno30: Optional[float] = Field(1.0, description='Initial NO3 concentration in the sediment layer (g/m3)')

bpo40 class-attribute instance-attribute

bpo40: Optional[float] = Field(5.0, description='Initial PO4 concentration in the sediment layer (g/m3)')

bh2s0 class-attribute instance-attribute

bh2s0: Optional[float] = Field(250.0, description='Initial H2S concentration in the sediment layer (g/m3)')

bch40 class-attribute instance-attribute

bch40: Optional[float] = Field(40.0, description='Initial CH4 concentration in the sediment layer (g/m3)')

bpos0 class-attribute instance-attribute

bpos0: Optional[float] = Field(500.0, description='Initial POS (Particulate Organic Silica) concentration in the sediment layer (g/m3)')

bsa0 class-attribute instance-attribute

bsa0: Optional[float] = Field(500.0, description='Initial SA (Salinity) concentration in the sediment layer (g/m3)')

bpoc0 class-attribute instance-attribute

bpoc0: Optional[list] = Field([1000.0, 3000.0, 5000.0], description='Initial POC (Particulate Organic Carbon) concentrations for 3 classes (G1, G2, G3) in the sediment layer (g/m3)')

bpon0 class-attribute instance-attribute

bpon0: Optional[list] = Field([150.0, 500.0, 1500.0], description='Initial PON (Particulate Organic Nitrogen) concentrations for 3 classes (G1, G2, G3) in the sediment layer (g/m3)')

bpop0 class-attribute instance-attribute

bpop0: Optional[list] = Field([30.0, 300.0, 500.0], description='Initial POP (Particulate Organic Phosphorus) concentrations for 3 classes (G1, G2, G3) in the sediment layer (g/m3)')

bdz class-attribute instance-attribute

bdz: Optional[float] = Field(0.1, description='Sediment thickness (m)')

bvb class-attribute instance-attribute

bvb: Optional[float] = Field(1.37e-05, description='Burial rate (m/day)')

bsolid class-attribute instance-attribute

bsolid: Optional[list] = Field([0.5, 0.5], description='Sediment solid concentrations in Layer 1 and Layer 2 (Kg/L)')

bdiff class-attribute instance-attribute

bdiff: Optional[float] = Field(1.8e-07, description='Diffusion coefficient for sediment temperature (m2/s)')

btr class-attribute instance-attribute

btr: Optional[int] = Field(20, description='Reference temperature for sediment processes (°C)')

bvpmin class-attribute instance-attribute

bvpmin: Optional[float] = Field(3e-06, description='minimum particle mixing velocity coefficient (m.day-1)')

bvp class-attribute instance-attribute

bvp: Optional[float] = Field(0.00012, description='particle mixing velocity coefficient (m.day-1)')

bvd class-attribute instance-attribute

bvd: Optional[float] = Field(0.001, description='diffusion velocity coefficient (m.day-1)')

bktvp class-attribute instance-attribute

bktvp: Optional[float] = Field(1.117, description='temp. dependece of particle mixing velocity')

bktvd class-attribute instance-attribute

bktvd: Optional[float] = Field(1.08, description='temp. dependece of diffusion velocity')

bkst class-attribute instance-attribute

bkst: Optional[float] = Field(0.03, description='1st order decay rate of benthic stress  (day-1)')

bstmax class-attribute instance-attribute

bstmax: Optional[float] = Field(20.0, description='maximum value of benthic stress (day) (note: smaller than 1/bKST)')

bkhdo_vp class-attribute instance-attribute

bkhdo_vp: Optional[float] = Field(4.0, description='DO half-saturation of particle mixing (mg/L)')

bdoc_st class-attribute instance-attribute

bdoc_st: Optional[float] = Field(1.0, description='DO criteria for benthic stress (mg/L)')

banoxic class-attribute instance-attribute

banoxic: Optional[float] = Field(10.0, description='consective days of hypoxia causing maximum benthic stress (day)')

boxic class-attribute instance-attribute

boxic: Optional[float] = Field(45.0, description='time lag for bethos recovery from hypoxia event (day)')

bp2d class-attribute instance-attribute

bp2d: Optional[float] = Field(0.0, description='ratio from mixing coef. to diffusion coef. (benthos enhanced effect)')

bkc class-attribute instance-attribute

bkc: Optional[list] = Field([0.035, 0.0018, 0.0], description='decay rate of POC (3G class) at bTR (day-1)')

bkn class-attribute instance-attribute

bkn: Optional[list] = Field([0.035, 0.0018, 0.0], description='decay rate of PON (3G class) at bTR (day-1)')

bkp class-attribute instance-attribute

bkp: Optional[list] = Field([0.035, 0.0018, 0.0], description='decay rate of POP (3G class) at bTR (day-1)')

bktc class-attribute instance-attribute

bktc: Optional[list] = Field([1.1, 1.15, 1.17], description='temp. dependence of POC decay (oC-1)')

bktn class-attribute instance-attribute

bktn: Optional[list] = Field([1.1, 1.15, 1.17], description='temp. dependence of PON decay (oC-1)')

bktp class-attribute instance-attribute

bktp: Optional[list] = Field([1.1, 1.15, 1.17], description='temp. dependence of POP decay (oC-1)')

bfcp class-attribute instance-attribute

bfcp: Optional[list] = Field([0.35, 0.55, 0.01, 0.35, 0.55, 0.01, 0.35, 0.55, 0.01], description='Phyto POC into sed POC (G3,PB=1:3)')

bfnp class-attribute instance-attribute

bfnp: Optional[list] = Field([0.35, 0.55, 0.01, 0.35, 0.55, 0.01, 0.35, 0.55, 0.01], description='Phyto PON into sed PON (G3,PB=1:3)')

bfpp class-attribute instance-attribute

bfpp: Optional[list] = Field([0.35, 0.55, 0.01, 0.35, 0.55, 0.01, 0.35, 0.55, 0.01], description='Phyto POP into sed POP (G3,PB=1:3)')

bfcm class-attribute instance-attribute

bfcm: Optional[list] = Field([0.0, 0.43, 0.57], description='refractory POC into sed POC(3G)')

bfnm class-attribute instance-attribute

bfnm: Optional[list] = Field([0.0, 0.54, 0.46], description='refractory PON into sed PON(3G)')

bfpm class-attribute instance-attribute

bfpm: Optional[list] = Field([0.0, 0.43, 0.57], description='refractory POP into sed POP(3G)')

bknh4f class-attribute instance-attribute

bknh4f: Optional[float] = Field(0.2, description='NH4 reaction rate in freshwater  at bTR (1st layer) (m/day)')

bknh4s class-attribute instance-attribute

bknh4s: Optional[float] = Field(0.14, description='NH4 reaction rate in salty water at bTR (1st layer) (m/day)')

bktnh4 class-attribute instance-attribute

bktnh4: Optional[float] = Field(1.08, description='temp. dependency for NH4 reaction (oC-1)')

bkhnh4 class-attribute instance-attribute

bkhnh4: Optional[float] = Field(1.5, description='half-stauration NH4 for nitrification (g/m3)')

bkhdo_nh4 class-attribute instance-attribute

bkhdo_nh4: Optional[float] = Field(2.0, description='half-stauration DO for nitrification (g/m3)')

bpienh4 class-attribute instance-attribute

bpienh4: Optional[float] = Field(1.0, description='partition coefficients of NH4 in Layer 1 & 2 (Kg-1.L)')

bsaltn class-attribute instance-attribute

bsaltn: Optional[float] = Field(1.0, description='salinity criteria of fresh/salty water for NH4/NO3 reaction (PSU)')

bkno3f class-attribute instance-attribute

bkno3f: Optional[float] = Field(0.3, description='NO3 reaction rate in freshwater at bTR (1st layer) (m/day)')

bkno3s class-attribute instance-attribute

bkno3s: Optional[float] = Field(0.125, description='NO3 reaction rate in salty water at bTR (1st layer) (m/day)')

bkno3 class-attribute instance-attribute

bkno3: Optional[float] = Field(0.25, description='NO3 reaction rate (2nd layer) (m/day)')

bktno3 class-attribute instance-attribute

bktno3: Optional[float] = Field(1.08, description='temp. dependency for NO3 reaction (oC-1)')

bkh2sd class-attribute instance-attribute

bkh2sd: Optional[float] = Field(0.2, description='dissolved H2S reaction rate at bTR (1st layer) (m/day)')

bkh2sp class-attribute instance-attribute

bkh2sp: Optional[float] = Field(0.4, description='particulate H2S reaction rate at bTR (1st layer) (m/day)')

bkth2s class-attribute instance-attribute

bkth2s: Optional[float] = Field(1.08, description='temp. dependency for H2S reaction (oC-1)')

bpieh2ss class-attribute instance-attribute

bpieh2ss: Optional[float] = Field(100.0, description='partition coefficient of NH4 in Layer 1 (Kg-1.L)')

bpieh2sb class-attribute instance-attribute

bpieh2sb: Optional[float] = Field(100.0, description='partition coefficient of NH4 in Layer 2 (Kg-1.L)')

bkhdo_h2s class-attribute instance-attribute

bkhdo_h2s: Optional[float] = Field(8.0, description='O2 constant to normalize H2S oxidation (g[O2]/m3)')

bsaltc class-attribute instance-attribute

bsaltc: Optional[float] = Field(1.0, description='salinity criteria of fresh/salty water for carbon reaction (PSU)')

bkch4 class-attribute instance-attribute

bkch4: Optional[float] = Field(0.2, description='CH4 reaction rate at bTR (1st layer) (m/day)')

bktch4 class-attribute instance-attribute

bktch4: Optional[float] = Field(1.08, description='temp. dependency for CH4 reaction')

bkhdo_ch4 class-attribute instance-attribute

bkhdo_ch4: Optional[float] = Field(0.2, description='half-saturation DO for CH4 oxidation (g[O2]/m3)')

bo2n class-attribute instance-attribute

bo2n: Optional[float] = Field(2.86, description='oxygen to nitrogen ratio in sediment (denitrification)')

bpiepo4 class-attribute instance-attribute

bpiepo4: Optional[float] = Field(50.0, description='partition coefficient of PO4 in Layer 2 (Kg-1.L)')

bkopo4f class-attribute instance-attribute

bkopo4f: Optional[float] = Field(3000.0, description='oxygen dependency for PO4 sorption in freshwater in Layer 1')

bkopo4s class-attribute instance-attribute

bkopo4s: Optional[float] = Field(300.0, description='oxygen dependency for PO4 sorption in salty water in Layer 1')

bdoc_po4 class-attribute instance-attribute

bdoc_po4: Optional[float] = Field(1.0, description='DO criteria for PO4 sorptiona (g[O2]/m3)')

bsaltp class-attribute instance-attribute

bsaltp: Optional[float] = Field(1.0, description='salinity criteria of fresh/salty water for PO4 partition (PSU)')

bks class-attribute instance-attribute

bks: Optional[float] = Field(0.5, description='decay rate of POS (3G class) at bTR')

bkts class-attribute instance-attribute

bkts: Optional[float] = Field(1.1, description='temp. dependence of POS decay')

bsisat class-attribute instance-attribute

bsisat: Optional[float] = Field(40.0, description='silica saturation conc. in pore water (g[Si]/m3)')

bpiesi class-attribute instance-attribute

bpiesi: Optional[float] = Field(100.0, description='partition coefficient of silica in Layer 2 (Kg-1.L)')

bkosi class-attribute instance-attribute

bkosi: Optional[float] = Field(10.0, description='oxygen dependency for silica sorption in Layer 1')

bkhpos class-attribute instance-attribute

bkhpos: Optional[float] = Field(50000.0, description='POS half saturation for POS dissolution (g/m3)')

bdoc_si class-attribute instance-attribute

bdoc_si: Optional[float] = Field(1.0, description='DO criteria for silica sorptiona (g[O2]/m3)')

bjposa class-attribute instance-attribute

bjposa: Optional[float] = Field(0.0, description='additional POS flux associated with POM detrius beside algea (g.m-2.day-1)')

bfcs class-attribute instance-attribute

bfcs: Optional[list] = Field([0.65, 0.255, 0.095], description='SAV POC into 3G sed 3G POC')

bfns class-attribute instance-attribute

bfns: Optional[list] = Field([0.65, 0.3, 0.05], description='SAV PON into 3G sed 3G PON')

bfps class-attribute instance-attribute

bfps: Optional[list] = Field([0.65, 0.255, 0.095], description='SAV POP into 3G sed 3G POP')

bfcv class-attribute instance-attribute

bfcv: Optional[list] = Field([0.65, 0.255, 0.095, 0.65, 0.255, 0.095, 0.65, 0.255, 0.095], description='VEG POC into sed POC (G3,PB=1:3)')

bfnv class-attribute instance-attribute

bfnv: Optional[list] = Field([0.65, 0.3, 0.05, 0.65, 0.3, 0.05, 0.65, 0.3, 0.05], description='VEG PON into sed PON (G3,PB=1:3)')

bfpv class-attribute instance-attribute

bfpv: Optional[list] = Field([0.65, 0.255, 0.095, 0.65, 0.255, 0.095, 0.65, 0.255, 0.095], description='VEG POP into sed POP (G3,PB=1:3)')

Functions

validate_btemp0 classmethod

validate_btemp0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("btemp0")
@classmethod
def validate_btemp0(cls, v):
    if v < -2 or v > 40:
        raise ValueError("btemp0 must be between -2 and 40")
    return v

validate_bstc0 classmethod

validate_bstc0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("bstc0")
@classmethod
def validate_bstc0(cls, v):
    if v < 0:
        raise ValueError("bstc0 must be non-negative")
    return v

validate_bstr0 classmethod

validate_bstr0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("bstr0")
@classmethod
def validate_bstr0(cls, v):
    if v < 0:
        raise ValueError("bstr0 must be non-negative")
    return v

validate_bthp0 classmethod

validate_bthp0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("bthp0")
@classmethod
def validate_bthp0(cls, v):
    if v < 0:
        raise ValueError("bthp0 must be non-negative")
    return v

validate_btox0 classmethod

validate_btox0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("btox0")
@classmethod
def validate_btox0(cls, v):
    if v < 0:
        raise ValueError("btox0 must be non-negative")
    return v

validate_bnh40 classmethod

validate_bnh40(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("bnh40")
@classmethod
def validate_bnh40(cls, v):
    if v < 0:
        raise ValueError("bnh40 must be non-negative")
    return v

validate_bno30 classmethod

validate_bno30(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("bno30")
@classmethod
def validate_bno30(cls, v):
    if v < 0:
        raise ValueError("bno30 must be non-negative")
    return v

validate_bpo40 classmethod

validate_bpo40(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("bpo40")
@classmethod
def validate_bpo40(cls, v):
    if v < 0:
        raise ValueError("bpo40 must be non-negative")
    return v

validate_bh2s0 classmethod

validate_bh2s0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("bh2s0")
@classmethod
def validate_bh2s0(cls, v):
    if v < 0:
        raise ValueError("bh2s0 must be non-negative")
    return v

validate_bch40 classmethod

validate_bch40(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("bch40")
@classmethod
def validate_bch40(cls, v):
    if v < 0:
        raise ValueError("bch40 must be non-negative")
    return v

validate_bpos0 classmethod

validate_bpos0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("bpos0")
@classmethod
def validate_bpos0(cls, v):
    if v < 0:
        raise ValueError("bpos0 must be non-negative")
    return v

validate_bsa0 classmethod

validate_bsa0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("bsa0")
@classmethod
def validate_bsa0(cls, v):
    if v < 0:
        raise ValueError("bsa0 must be non-negative")
    return v

validate_bpoc0 classmethod

validate_bpoc0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("bpoc0")
@classmethod
def validate_bpoc0(cls, v):
    if len(v) != 3 or any(x < 0 for x in v):
        raise ValueError("bpoc0 must be a list of 3 non-negative values")
    return v

validate_bpon0 classmethod

validate_bpon0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("bpon0")
@classmethod
def validate_bpon0(cls, v):
    if len(v) != 3 or any(x < 0 for x in v):
        raise ValueError("bpon0 must be a list of 3 non-negative values")
    return v

validate_bpop0 classmethod

validate_bpop0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("bpop0")
@classmethod
def validate_bpop0(cls, v):
    if len(v) != 3 or any(x < 0 for x in v):
        raise ValueError("bpop0 must be a list of 3 non-negative values")
    return v

validate_bdz classmethod

validate_bdz(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("bdz")
@classmethod
def validate_bdz(cls, v):
    if v <= 0:
        raise ValueError("bdz must be positive")
    return v

validate_bvb classmethod

validate_bvb(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("bvb")
@classmethod
def validate_bvb(cls, v):
    if v < 0:
        raise ValueError("bvb must be non-negative")
    return v

validate_bsolid classmethod

validate_bsolid(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("bsolid")
@classmethod
def validate_bsolid(cls, v):
    if len(v) != 2 or any(x <= 0 or x > 1 for x in v):
        raise ValueError("bsolid must be a list of 2 values between 0 and 1")
    return v

validate_bdiff classmethod

validate_bdiff(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("bdiff")
@classmethod
def validate_bdiff(cls, v):
    if v <= 0:
        raise ValueError("bdiff must be positive")
    return v

validate_btr classmethod

validate_btr(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("btr")
@classmethod
def validate_btr(cls, v):
    if v < 0 or v > 40:
        raise ValueError("btr must be between 0 and 40")
    return v

Silica

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/icm.py
class Silica(NamelistBaseModel):
    fsp: Optional[list] = Field(
        [0.9, 0.1], description="fractions of diatom silica into (SU,SA)"
    )
    fsm: Optional[list] = Field(
        [0.5, 0.5], description="fractions of diatom metabolism Si into (SU,SA)"
    )
    ks: Optional[float] = Field(
        0.03, description="dissolution rate of SU at TRS (day-1)"
    )
    trs: Optional[float] = Field(
        20.0, description="reference temp. for SU dissolution (oC)"
    )
    ktrs: Optional[float] = Field(
        0.092, description="temp. dependence for SU dissolution (oC-1)"
    )
    khs: Optional[list] = Field(
        [0.05, 0.0, 0.0],
        description="silica half saturation (mg/L); (0.0: no Si limitation)",
    )
    s2c: Optional[list] = Field(
        [0.5, 0.0, 0.0],
        description="silica to carbon ratio for phytolankton; (0.0: no Si uptake)",
    )
    ksap: Optional[float] = Field(
        0.0, description="coefficient relating Silicate(SA) sorption to TSS"
    )

Attributes

fsp class-attribute instance-attribute

fsp: Optional[list] = Field([0.9, 0.1], description='fractions of diatom silica into (SU,SA)')

fsm class-attribute instance-attribute

fsm: Optional[list] = Field([0.5, 0.5], description='fractions of diatom metabolism Si into (SU,SA)')

ks class-attribute instance-attribute

ks: Optional[float] = Field(0.03, description='dissolution rate of SU at TRS (day-1)')

trs class-attribute instance-attribute

trs: Optional[float] = Field(20.0, description='reference temp. for SU dissolution (oC)')

ktrs class-attribute instance-attribute

ktrs: Optional[float] = Field(0.092, description='temp. dependence for SU dissolution (oC-1)')

khs class-attribute instance-attribute

khs: Optional[list] = Field([0.05, 0.0, 0.0], description='silica half saturation (mg/L); (0.0: no Si limitation)')

s2c class-attribute instance-attribute

s2c: Optional[list] = Field([0.5, 0.0, 0.0], description='silica to carbon ratio for phytolankton; (0.0: no Si uptake)')

ksap class-attribute instance-attribute

ksap: Optional[float] = Field(0.0, description='coefficient relating Silicate(SA) sorption to TSS')

Stem

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/icm.py
class Stem(NamelistBaseModel):
    sn2c: Optional[float] = Field(
        0.09,
        description="Nitrogen to carbon ratio of submerged aquatic vegetation (SAV)",
    )
    sp2c: Optional[float] = Field(0.01, description="Phosphorus to carbon ratio")
    so2c: Optional[float] = Field(2.67, description="Oxygen to carbon ratio")

    @field_validator("sn2c")
    @classmethod
    def validate_sn2c(cls, v):
        if v < 0 or v > 1:
            raise ValueError("sn2c must be between 0 and 1")
        return v

    @field_validator("sp2c")
    @classmethod
    def validate_sp2c(cls, v):
        if v < 0 or v > 1:
            raise ValueError("sp2c must be between 0 and 1")
        return v

    @field_validator("so2c")
    @classmethod
    def validate_so2c(cls, v):
        if v <= 0:
            raise ValueError("so2c must be greater than 0")
        return v

Attributes

sn2c class-attribute instance-attribute

sn2c: Optional[float] = Field(0.09, description='Nitrogen to carbon ratio of submerged aquatic vegetation (SAV)')

sp2c class-attribute instance-attribute

sp2c: Optional[float] = Field(0.01, description='Phosphorus to carbon ratio')

so2c class-attribute instance-attribute

so2c: Optional[float] = Field(2.67, description='Oxygen to carbon ratio')

Functions

validate_sn2c classmethod

validate_sn2c(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("sn2c")
@classmethod
def validate_sn2c(cls, v):
    if v < 0 or v > 1:
        raise ValueError("sn2c must be between 0 and 1")
    return v

validate_sp2c classmethod

validate_sp2c(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("sp2c")
@classmethod
def validate_sp2c(cls, v):
    if v < 0 or v > 1:
        raise ValueError("sp2c must be between 0 and 1")
    return v

validate_so2c classmethod

validate_so2c(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("so2c")
@classmethod
def validate_so2c(cls, v):
    if v <= 0:
        raise ValueError("so2c must be greater than 0")
    return v

Veg

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/icm.py
2188
2189
2190
2191
2192
2193
2194
2195
2196
2197
2198
2199
2200
2201
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236
2237
2238
2239
2240
2241
2242
2243
2244
2245
2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256
2257
2258
2259
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296
2297
2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319
2320
2321
2322
2323
2324
2325
2326
2327
2328
2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
class Veg(NamelistBaseModel):
    vpatch0: Optional[int] = Field(
        -999,
        description="Region flag for VEG. (1: ON for all elements; -999: spatial distribution)",
    )
    vtleaf0: Optional[list] = Field(
        [100.0, 100.0, 100.0],
        description="Initial concentration for total vegetation leaf biomass (3 values for different vegetation types)",
    )
    vtstem0: Optional[list] = Field(
        [100.0, 100.0, 100.0],
        description="Initial concentration for total vegetation stem biomass (3 values for different vegetation types)",
    )
    vtroot0: Optional[list] = Field(
        [30.0, 30.0, 30.0],
        description="Initial concentration for total vegetation root biomass (3 values for different vegetation types)",
    )
    vgpm: Optional[list] = Field(
        [0.1, 0.1, 0.1],
        description="Maximum growth rate for vegetation (day^-1) (3 values for different vegetation types)",
    )
    vfam: Optional[list] = Field(
        [0.2, 0.2, 0.2],
        description="Fractions of leaf production allocated to active metabolism (3 values for different vegetation types)",
    )
    vtgp: Optional[list] = Field(
        [32.0, 32.0, 32.0],
        description="Optimal growth temperature for vegetation (°C) (3 values for different vegetation types)",
    )
    vktgp: Optional[list] = Field(
        [0.003, 0.003, 0.003, 0.005, 0.005, 0.005],
        description="Temperature dependence coefficients for growth (6 values: 3 for T<=vTGP and 3 for T>vTGP, for different vegetation types)",
    )
    vfcp: Optional[list] = Field(
        [0.6, 0.6, 0.6, 0.3, 0.3, 0.3, 0.1, 0.1, 0.1],
        description="Fractions of production allocated to leaf/stem/root biomass (9 values: 3 for each vegetation type, 3 for each biomass component)",
    )
    vmtb: Optional[list] = Field(
        [0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.01, 0.01, 0.01],
        description="Metabolism rates for leaf/stem/root (9 values: 3 for each vegetation type, 3 for each biomass component)",
    )
    vtmt: Optional[list] = Field(
        [20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0],
        description="Reference temperatures for leaf/stem/root metabolism (9 values: 3 for each vegetation type, 3 for each biomass component)",
    )
    vktmt: Optional[list] = Field(
        [0.069, 0.069, 0.069, 0.069, 0.069, 0.069, 0.069, 0.069, 0.069],
        description="Temperature dependence coefficients for leaf/stem/root metabolism (9 values: 3 for each vegetation type, 3 for each biomass component)",
    )
    vfnm: Optional[list] = Field(
        [0.05, 0.05, 0.05, 0.15, 0.15, 0.15, 0.3, 0.3, 0.3, 0.5, 0.5, 0.5],
        description="Fractions of metabolism N into RPON, LPON, DON, NH4 (12 values: 3 for each vegetation type, 4 for each N form)",
    )
    vfpm: Optional[list] = Field(
        [0.05, 0.05, 0.05, 0.1, 0.1, 0.1, 0.35, 0.35, 0.35, 0.5, 0.5, 0.5],
        description="Fractions of metabolism P into RPOP, LPOP, DOP, PO4 (12 values: 3 for each vegetation type, 4 for each P form)",
    )
    vfcm: Optional[list] = Field(
        [0.05, 0.05, 0.05, 0.15, 0.15, 0.15, 0.3, 0.3, 0.3, 0.5, 0.5, 0.5],
        description="Fractions of metabolism C into RPOC, LPOC, DOC, CO2 (12 values: 3 for each vegetation type, 4 for each C form)",
    )
    ivnc: Optional[int] = Field(
        1,
        description="Flag for recycled vegetation N destination (0: sediment; 1: water)",
    )
    ivpc: Optional[int] = Field(
        1,
        description="Flag for recycled vegetation P destination (0: sediment; 1: water)",
    )
    vkhns: Optional[list] = Field(
        [0.1, 0.1, 0.1],
        description="Nitrogen half-saturation constant in sediments (3 values for different vegetation types)",
    )
    vkhps: Optional[list] = Field(
        [0.01, 0.01, 0.01],
        description="Phosphorus half-saturation constant in sediments (3 values for different vegetation types)",
    )
    vscr: Optional[list] = Field(
        [35.0, 35.0, 35.0],
        description="Reference salinity for computing vegetation growth (3 values for different vegetation types)",
    )
    vsopt: Optional[list] = Field(
        [35.0, 15.0, 0.0],
        description="Optimal salinity for vegetation growth (3 values for different vegetation types)",
    )
    vinun: Optional[list] = Field(
        [1.0, 1.0, 1.0],
        description="Reference value for inundation stress (nondimensional) (3 values for different vegetation types)",
    )
    ivns: Optional[int] = Field(
        1, description="Flag for N limitation on vegetation growth (0: OFF; 1: ON)"
    )
    ivps: Optional[int] = Field(
        1, description="Flag for P limitation on vegetation growth (0: OFF; 1: ON)"
    )
    ivmrt: Optional[int] = Field(
        0, description="Flag for vegetation mortality term (0: OFF; 1: ON)"
    )
    vtmr: Optional[list] = Field(
        [17.0, 17.0, 17.0, 17.0, 17.0, 17.0],
        description="Reference temperatures for leaf/stem mortality (6 values: 3 for each vegetation type, 2 for leaf/stem)",
    )
    vktmr: Optional[list] = Field(
        [4.0, 4.0, 4.0, 4.0, 4.0, 4.0],
        description="Temperature dependence coefficients for leaf/stem mortality (6 values: 3 for each vegetation type, 2 for leaf/stem)",
    )
    vmr0: Optional[list] = Field(
        [12.8, 12.8, 12.8, 12.8, 12.8, 12.8],
        description="Base value of temperature effect on mortality (6 values: 3 for each vegetation type, 2 for leaf/stem)",
    )
    vmrcr: Optional[list] = Field(
        [15.0, 15.0, 15.0, 15.0, 15.0, 15.0],
        description="Reference value for computing mortality (6 values: 3 for each vegetation type, 2 for leaf/stem)",
    )
    valpha: Optional[list] = Field(
        [0.006, 0.006, 0.006],
        description="Initial slope of P-I curve (3 values for different vegetation types)",
    )
    vke: Optional[list] = Field(
        [0.045, 0.045, 0.045],
        description="Light attenuation coefficient from vegetation absorption (3 values for different vegetation types)",
    )
    vht0: Optional[list] = Field(
        [0.054, 0.054, 0.054],
        description="Base vegetation canopy height (3 values for different vegetation types)",
    )
    vcrit: Optional[list] = Field(
        [250.0, 250.0, 250.0],
        description="Critical mass for computing vegetation height (3 values for different vegetation types)",
    )
    v2ht: Optional[list] = Field(
        [0.0036, 0.0036, 0.0036, 0.001, 0.001, 0.001],
        description="Coefficients to convert mass to canopy height (6 values: 3 for each vegetation type, 2 for different conditions)",
    )
    vc2dw: Optional[list] = Field(
        [0.38, 0.38, 0.38],
        description="Carbon to dry weight ratio of vegetation (3 values for different vegetation types)",
    )
    v2den: Optional[list] = Field(
        [10, 10, 10],
        description="Coefficient for computing vegetation density (3 values for different vegetation types)",
    )
    vp2c: Optional[list] = Field(
        [0.01, 0.01, 0.01],
        description="Phosphorus to carbon ratio in vegetation (3 values for different vegetation types)",
    )
    vn2c: Optional[list] = Field(
        [0.09, 0.09, 0.09],
        description="Nitrogen to carbon ratio in vegetation (3 values for different vegetation types)",
    )
    vo2c: Optional[list] = Field(
        [2.67, 2.67, 2.67],
        description="Oxygen to carbon ratio in vegetation (3 values for different vegetation types)",
    )

    @field_validator("vpatch0")
    @classmethod
    def check_vpatch0(cls, v):
        if v not in [1, -999]:
            raise ValueError("vpatch0 must be either 1 or -999")
        return v

    @field_validator("vtleaf0")
    @classmethod
    def check_vtleaf0(cls, v):
        if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
            raise ValueError("vtleaf0 must be a list of 3 positive numbers")
        return v

    @field_validator("vtstem0")
    @classmethod
    def check_vtstem0(cls, v):
        if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
            raise ValueError("vtstem0 must be a list of 3 positive numbers")
        return v

    @field_validator("vtroot0")
    @classmethod
    def check_vtroot0(cls, v):
        if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
            raise ValueError("vtroot0 must be a list of 3 positive numbers")
        return v

    @field_validator("vgpm")
    @classmethod
    def check_vgpm(cls, v):
        if len(v) != 3 or not all(isinstance(x, (int, float)) and 0 < x < 1 for x in v):
            raise ValueError("vgpm must be a list of 3 numbers between 0 and 1")
        return v

    @field_validator("vfam")
    @classmethod
    def check_vfam(cls, v):
        if len(v) != 3 or not all(isinstance(x, (int, float)) and 0 < x < 1 for x in v):
            raise ValueError("vfam must be a list of 3 numbers between 0 and 1")
        return v

    @field_validator("vtgp")
    @classmethod
    def check_vtgp(cls, v):
        if len(v) != 3 or not all(
            isinstance(x, (int, float)) and 0 < x < 50 for x in v
        ):
            raise ValueError("vtgp must be a list of 3 numbers between 0 and 50")
        return v

    @field_validator("vktgp")
    @classmethod
    def check_vktgp(cls, v):
        if len(v) != 6 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
            raise ValueError("vktgp must be a list of 6 positive numbers")
        return v

    @field_validator("vfcp")
    @classmethod
    def check_vfcp(cls, v):
        if len(v) != 9 or not all(
            isinstance(x, (int, float)) and 0 <= x <= 1 for x in v
        ):
            raise ValueError("vfcp must be a list of 9 numbers between 0 and 1")
        return v

    @field_validator("vmtb")
    @classmethod
    def check_vmtb(cls, v):
        if len(v) != 9 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
            raise ValueError("vmtb must be a list of 9 positive numbers")
        return v

    @field_validator("vtmt")
    @classmethod
    def check_vtmt(cls, v):
        if len(v) != 9 or not all(
            isinstance(x, (int, float)) and 0 < x < 50 for x in v
        ):
            raise ValueError("vtmt must be a list of 9 numbers between 0 and 50")
        return v

    @field_validator("vktmt")
    @classmethod
    def check_vktmt(cls, v):
        if len(v) != 9 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
            raise ValueError("vktmt must be a list of 9 positive numbers")
        return v

    @field_validator("vfnm")
    @classmethod
    def check_vfnm(cls, v):
        if len(v) != 12 or not all(
            isinstance(x, (int, float)) and 0 <= x <= 1 for x in v
        ):
            raise ValueError("vfnm must be a list of 12 numbers between 0 and 1")
        return v

    @field_validator("vfpm")
    @classmethod
    def check_vfpm(cls, v):
        if len(v) != 12 or not all(
            isinstance(x, (int, float)) and 0 <= x <= 1 for x in v
        ):
            raise ValueError("vfpm must be a list of 12 numbers between 0 and 1")
        return v

    @field_validator("vfcm")
    @classmethod
    def check_vfcm(cls, v):
        if len(v) != 12 or not all(
            isinstance(x, (int, float)) and 0 <= x <= 1 for x in v
        ):
            raise ValueError("vfcm must be a list of 12 numbers between 0 and 1")
        return v

    @field_validator("ivnc")
    @classmethod
    def check_ivnc(cls, v):
        if v not in [0, 1]:
            raise ValueError("ivnc must be either 0 or 1")
        return v

    @field_validator("ivpc")
    @classmethod
    def check_ivpc(cls, v):
        if v not in [0, 1]:
            raise ValueError("ivpc must be either 0 or 1")
        return v

    @field_validator("vkhns")
    @classmethod
    def check_vkhns(cls, v):
        if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
            raise ValueError("vkhns must be a list of 3 positive numbers")
        return v

    @field_validator("vkhps")
    @classmethod
    def check_vkhps(cls, v):
        if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
            raise ValueError("vkhps must be a list of 3 positive numbers")
        return v

    @field_validator("vscr")
    @classmethod
    def check_vscr(cls, v):
        if len(v) != 3 or not all(
            isinstance(x, (int, float)) and 0 <= x <= 40 for x in v
        ):
            raise ValueError("vscr must be a list of 3 numbers between 0 and 40")
        return v

    @field_validator("vsopt")
    @classmethod
    def check_vsopt(cls, v):
        if len(v) != 3 or not all(
            isinstance(x, (int, float)) and 0 <= x <= 40 for x in v
        ):
            raise ValueError("vsopt must be a list of 3 numbers between 0 and 40")
        return v

    @field_validator("vinun")
    @classmethod
    def check_vinun(cls, v):
        if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
            raise ValueError("vinun must be a list of 3 positive numbers")
        return v

    @field_validator("ivns")
    @classmethod
    def check_ivns(cls, v):
        if v not in [0, 1]:
            raise ValueError("ivns must be either 0 or 1")
        return v

    @field_validator("ivps")
    @classmethod
    def check_ivps(cls, v):
        if v not in [0, 1]:
            raise ValueError("ivps must be either 0 or 1")
        return v

    @field_validator("ivmrt")
    @classmethod
    def check_ivmrt(cls, v):
        if v not in [0, 1]:
            raise ValueError("ivmrt must be either 0 or 1")
        return v

    @field_validator("vtmr")
    @classmethod
    def check_vtmr(cls, v):
        if len(v) != 6 or not all(
            isinstance(x, (int, float)) and 0 < x < 50 for x in v
        ):
            raise ValueError("vtmr must be a list of 6 numbers between 0 and 50")
        return v

    @field_validator("vktmr")
    @classmethod
    def check_vktmr(cls, v):
        if len(v) != 6 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
            raise ValueError("vktmr must be a list of 6 positive numbers")
        return v

    @field_validator("vmr0")
    @classmethod
    def check_vmr0(cls, v):
        if len(v) != 6 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
            raise ValueError("vmr0 must be a list of 6 positive numbers")
        return v

    @field_validator("vmrcr")
    @classmethod
    def check_vmrcr(cls, v):
        if len(v) != 6 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
            raise ValueError("vmrcr must be a list of 6 positive numbers")
        return v

    @field_validator("valpha")
    @classmethod
    def check_valpha(cls, v):
        if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
            raise ValueError("valpha must be a list of 3 positive numbers")
        return v

    @field_validator("vke")
    @classmethod
    def check_vke(cls, v):
        if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
            raise ValueError("vke must be a list of 3 positive numbers")
        return v

    @field_validator("vht0")
    @classmethod
    def check_vht0(cls, v):
        if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
            raise ValueError("vht0 must be a list of 3 positive numbers")
        return v

    @field_validator("vcrit")
    @classmethod
    def check_vcrit(cls, v):
        if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
            raise ValueError("vcrit must be a list of 3 positive numbers")
        return v

    @field_validator("v2ht")
    @classmethod
    def check_v2ht(cls, v):
        if len(v) != 6 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
            raise ValueError("v2ht must be a list of 6 positive numbers")
        return v

    @field_validator("vc2dw")
    @classmethod
    def check_vc2dw(cls, v):
        if len(v) != 3 or not all(isinstance(x, (int, float)) and 0 < x < 1 for x in v):
            raise ValueError("vc2dw must be a list of 3 numbers between 0 and 1")
        return v

    @field_validator("v2den")
    @classmethod
    def check_v2den(cls, v):
        if len(v) != 3 or not all(isinstance(x, int) and x > 0 for x in v):
            raise ValueError("v2den must be a list of 3 positive integers")
        return v

    @field_validator("vp2c")
    @classmethod
    def check_vp2c(cls, v):
        if len(v) != 3 or not all(isinstance(x, (int, float)) and 0 < x < 1 for x in v):
            raise ValueError("vp2c must be a list of 3 numbers between 0 and 1")
        return v

    @field_validator("vn2c")
    @classmethod
    def check_vn2c(cls, v):
        if len(v) != 3 or not all(isinstance(x, (int, float)) and 0 < x < 1 for x in v):
            raise ValueError("vn2c must be a list of 3 numbers between 0 and 1")
        return v

    @field_validator("vo2c")
    @classmethod
    def check_vo2c(cls, v):
        if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
            raise ValueError("vo2c must be a list of 3 positive numbers")
        return v

    @model_validator(mode="after")
    def check_vfcp_sum(self):
        for i in range(3):
            if abs(sum(self.vfcp[i::3]) - 1) > 1e-6:
                raise ValueError(
                    f"Sum of vfcp values for vegetation type {i+1} must be 1"
                )
        return self

    @model_validator(mode="after")
    def check_vfnm_sum(self):
        for i in range(3):
            if abs(sum(self.vfnm[i::3]) - 1) > 1e-6:
                raise ValueError(
                    f"Sum of vfnm values for vegetation type {i+1} must be 1"
                )
        return self

    @model_validator(mode="after")
    def check_vfpm_sum(self):
        for i in range(3):
            if abs(sum(self.vfpm[i::3]) - 1) > 1e-6:
                raise ValueError(
                    f"Sum of vfpm values for vegetation type {i+1} must be 1"
                )
        return self

    @model_validator(mode="after")
    def check_vfcm_sum(self):
        for i in range(3):
            if abs(sum(self.vfcm[i::3]) - 1) > 1e-6:
                raise ValueError(
                    f"Sum of vfcm values for vegetation type {i+1} must be 1"
                )
        return self

Attributes

vpatch0 class-attribute instance-attribute

vpatch0: Optional[int] = Field(-999, description='Region flag for VEG. (1: ON for all elements; -999: spatial distribution)')

vtleaf0 class-attribute instance-attribute

vtleaf0: Optional[list] = Field([100.0, 100.0, 100.0], description='Initial concentration for total vegetation leaf biomass (3 values for different vegetation types)')

vtstem0 class-attribute instance-attribute

vtstem0: Optional[list] = Field([100.0, 100.0, 100.0], description='Initial concentration for total vegetation stem biomass (3 values for different vegetation types)')

vtroot0 class-attribute instance-attribute

vtroot0: Optional[list] = Field([30.0, 30.0, 30.0], description='Initial concentration for total vegetation root biomass (3 values for different vegetation types)')

vgpm class-attribute instance-attribute

vgpm: Optional[list] = Field([0.1, 0.1, 0.1], description='Maximum growth rate for vegetation (day^-1) (3 values for different vegetation types)')

vfam class-attribute instance-attribute

vfam: Optional[list] = Field([0.2, 0.2, 0.2], description='Fractions of leaf production allocated to active metabolism (3 values for different vegetation types)')

vtgp class-attribute instance-attribute

vtgp: Optional[list] = Field([32.0, 32.0, 32.0], description='Optimal growth temperature for vegetation (°C) (3 values for different vegetation types)')

vktgp class-attribute instance-attribute

vktgp: Optional[list] = Field([0.003, 0.003, 0.003, 0.005, 0.005, 0.005], description='Temperature dependence coefficients for growth (6 values: 3 for T<=vTGP and 3 for T>vTGP, for different vegetation types)')

vfcp class-attribute instance-attribute

vfcp: Optional[list] = Field([0.6, 0.6, 0.6, 0.3, 0.3, 0.3, 0.1, 0.1, 0.1], description='Fractions of production allocated to leaf/stem/root biomass (9 values: 3 for each vegetation type, 3 for each biomass component)')

vmtb class-attribute instance-attribute

vmtb: Optional[list] = Field([0.02, 0.02, 0.02, 0.02, 0.02, 0.02, 0.01, 0.01, 0.01], description='Metabolism rates for leaf/stem/root (9 values: 3 for each vegetation type, 3 for each biomass component)')

vtmt class-attribute instance-attribute

vtmt: Optional[list] = Field([20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0, 20.0], description='Reference temperatures for leaf/stem/root metabolism (9 values: 3 for each vegetation type, 3 for each biomass component)')

vktmt class-attribute instance-attribute

vktmt: Optional[list] = Field([0.069, 0.069, 0.069, 0.069, 0.069, 0.069, 0.069, 0.069, 0.069], description='Temperature dependence coefficients for leaf/stem/root metabolism (9 values: 3 for each vegetation type, 3 for each biomass component)')

vfnm class-attribute instance-attribute

vfnm: Optional[list] = Field([0.05, 0.05, 0.05, 0.15, 0.15, 0.15, 0.3, 0.3, 0.3, 0.5, 0.5, 0.5], description='Fractions of metabolism N into RPON, LPON, DON, NH4 (12 values: 3 for each vegetation type, 4 for each N form)')

vfpm class-attribute instance-attribute

vfpm: Optional[list] = Field([0.05, 0.05, 0.05, 0.1, 0.1, 0.1, 0.35, 0.35, 0.35, 0.5, 0.5, 0.5], description='Fractions of metabolism P into RPOP, LPOP, DOP, PO4 (12 values: 3 for each vegetation type, 4 for each P form)')

vfcm class-attribute instance-attribute

vfcm: Optional[list] = Field([0.05, 0.05, 0.05, 0.15, 0.15, 0.15, 0.3, 0.3, 0.3, 0.5, 0.5, 0.5], description='Fractions of metabolism C into RPOC, LPOC, DOC, CO2 (12 values: 3 for each vegetation type, 4 for each C form)')

ivnc class-attribute instance-attribute

ivnc: Optional[int] = Field(1, description='Flag for recycled vegetation N destination (0: sediment; 1: water)')

ivpc class-attribute instance-attribute

ivpc: Optional[int] = Field(1, description='Flag for recycled vegetation P destination (0: sediment; 1: water)')

vkhns class-attribute instance-attribute

vkhns: Optional[list] = Field([0.1, 0.1, 0.1], description='Nitrogen half-saturation constant in sediments (3 values for different vegetation types)')

vkhps class-attribute instance-attribute

vkhps: Optional[list] = Field([0.01, 0.01, 0.01], description='Phosphorus half-saturation constant in sediments (3 values for different vegetation types)')

vscr class-attribute instance-attribute

vscr: Optional[list] = Field([35.0, 35.0, 35.0], description='Reference salinity for computing vegetation growth (3 values for different vegetation types)')

vsopt class-attribute instance-attribute

vsopt: Optional[list] = Field([35.0, 15.0, 0.0], description='Optimal salinity for vegetation growth (3 values for different vegetation types)')

vinun class-attribute instance-attribute

vinun: Optional[list] = Field([1.0, 1.0, 1.0], description='Reference value for inundation stress (nondimensional) (3 values for different vegetation types)')

ivns class-attribute instance-attribute

ivns: Optional[int] = Field(1, description='Flag for N limitation on vegetation growth (0: OFF; 1: ON)')

ivps class-attribute instance-attribute

ivps: Optional[int] = Field(1, description='Flag for P limitation on vegetation growth (0: OFF; 1: ON)')

ivmrt class-attribute instance-attribute

ivmrt: Optional[int] = Field(0, description='Flag for vegetation mortality term (0: OFF; 1: ON)')

vtmr class-attribute instance-attribute

vtmr: Optional[list] = Field([17.0, 17.0, 17.0, 17.0, 17.0, 17.0], description='Reference temperatures for leaf/stem mortality (6 values: 3 for each vegetation type, 2 for leaf/stem)')

vktmr class-attribute instance-attribute

vktmr: Optional[list] = Field([4.0, 4.0, 4.0, 4.0, 4.0, 4.0], description='Temperature dependence coefficients for leaf/stem mortality (6 values: 3 for each vegetation type, 2 for leaf/stem)')

vmr0 class-attribute instance-attribute

vmr0: Optional[list] = Field([12.8, 12.8, 12.8, 12.8, 12.8, 12.8], description='Base value of temperature effect on mortality (6 values: 3 for each vegetation type, 2 for leaf/stem)')

vmrcr class-attribute instance-attribute

vmrcr: Optional[list] = Field([15.0, 15.0, 15.0, 15.0, 15.0, 15.0], description='Reference value for computing mortality (6 values: 3 for each vegetation type, 2 for leaf/stem)')

valpha class-attribute instance-attribute

valpha: Optional[list] = Field([0.006, 0.006, 0.006], description='Initial slope of P-I curve (3 values for different vegetation types)')

vke class-attribute instance-attribute

vke: Optional[list] = Field([0.045, 0.045, 0.045], description='Light attenuation coefficient from vegetation absorption (3 values for different vegetation types)')

vht0 class-attribute instance-attribute

vht0: Optional[list] = Field([0.054, 0.054, 0.054], description='Base vegetation canopy height (3 values for different vegetation types)')

vcrit class-attribute instance-attribute

vcrit: Optional[list] = Field([250.0, 250.0, 250.0], description='Critical mass for computing vegetation height (3 values for different vegetation types)')

v2ht class-attribute instance-attribute

v2ht: Optional[list] = Field([0.0036, 0.0036, 0.0036, 0.001, 0.001, 0.001], description='Coefficients to convert mass to canopy height (6 values: 3 for each vegetation type, 2 for different conditions)')

vc2dw class-attribute instance-attribute

vc2dw: Optional[list] = Field([0.38, 0.38, 0.38], description='Carbon to dry weight ratio of vegetation (3 values for different vegetation types)')

v2den class-attribute instance-attribute

v2den: Optional[list] = Field([10, 10, 10], description='Coefficient for computing vegetation density (3 values for different vegetation types)')

vp2c class-attribute instance-attribute

vp2c: Optional[list] = Field([0.01, 0.01, 0.01], description='Phosphorus to carbon ratio in vegetation (3 values for different vegetation types)')

vn2c class-attribute instance-attribute

vn2c: Optional[list] = Field([0.09, 0.09, 0.09], description='Nitrogen to carbon ratio in vegetation (3 values for different vegetation types)')

vo2c class-attribute instance-attribute

vo2c: Optional[list] = Field([2.67, 2.67, 2.67], description='Oxygen to carbon ratio in vegetation (3 values for different vegetation types)')

Functions

check_vpatch0 classmethod

check_vpatch0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vpatch0")
@classmethod
def check_vpatch0(cls, v):
    if v not in [1, -999]:
        raise ValueError("vpatch0 must be either 1 or -999")
    return v

check_vtleaf0 classmethod

check_vtleaf0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vtleaf0")
@classmethod
def check_vtleaf0(cls, v):
    if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
        raise ValueError("vtleaf0 must be a list of 3 positive numbers")
    return v

check_vtstem0 classmethod

check_vtstem0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vtstem0")
@classmethod
def check_vtstem0(cls, v):
    if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
        raise ValueError("vtstem0 must be a list of 3 positive numbers")
    return v

check_vtroot0 classmethod

check_vtroot0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vtroot0")
@classmethod
def check_vtroot0(cls, v):
    if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
        raise ValueError("vtroot0 must be a list of 3 positive numbers")
    return v

check_vgpm classmethod

check_vgpm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vgpm")
@classmethod
def check_vgpm(cls, v):
    if len(v) != 3 or not all(isinstance(x, (int, float)) and 0 < x < 1 for x in v):
        raise ValueError("vgpm must be a list of 3 numbers between 0 and 1")
    return v

check_vfam classmethod

check_vfam(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vfam")
@classmethod
def check_vfam(cls, v):
    if len(v) != 3 or not all(isinstance(x, (int, float)) and 0 < x < 1 for x in v):
        raise ValueError("vfam must be a list of 3 numbers between 0 and 1")
    return v

check_vtgp classmethod

check_vtgp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vtgp")
@classmethod
def check_vtgp(cls, v):
    if len(v) != 3 or not all(
        isinstance(x, (int, float)) and 0 < x < 50 for x in v
    ):
        raise ValueError("vtgp must be a list of 3 numbers between 0 and 50")
    return v

check_vktgp classmethod

check_vktgp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vktgp")
@classmethod
def check_vktgp(cls, v):
    if len(v) != 6 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
        raise ValueError("vktgp must be a list of 6 positive numbers")
    return v

check_vfcp classmethod

check_vfcp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vfcp")
@classmethod
def check_vfcp(cls, v):
    if len(v) != 9 or not all(
        isinstance(x, (int, float)) and 0 <= x <= 1 for x in v
    ):
        raise ValueError("vfcp must be a list of 9 numbers between 0 and 1")
    return v

check_vmtb classmethod

check_vmtb(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vmtb")
@classmethod
def check_vmtb(cls, v):
    if len(v) != 9 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
        raise ValueError("vmtb must be a list of 9 positive numbers")
    return v

check_vtmt classmethod

check_vtmt(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vtmt")
@classmethod
def check_vtmt(cls, v):
    if len(v) != 9 or not all(
        isinstance(x, (int, float)) and 0 < x < 50 for x in v
    ):
        raise ValueError("vtmt must be a list of 9 numbers between 0 and 50")
    return v

check_vktmt classmethod

check_vktmt(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vktmt")
@classmethod
def check_vktmt(cls, v):
    if len(v) != 9 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
        raise ValueError("vktmt must be a list of 9 positive numbers")
    return v

check_vfnm classmethod

check_vfnm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vfnm")
@classmethod
def check_vfnm(cls, v):
    if len(v) != 12 or not all(
        isinstance(x, (int, float)) and 0 <= x <= 1 for x in v
    ):
        raise ValueError("vfnm must be a list of 12 numbers between 0 and 1")
    return v

check_vfpm classmethod

check_vfpm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vfpm")
@classmethod
def check_vfpm(cls, v):
    if len(v) != 12 or not all(
        isinstance(x, (int, float)) and 0 <= x <= 1 for x in v
    ):
        raise ValueError("vfpm must be a list of 12 numbers between 0 and 1")
    return v

check_vfcm classmethod

check_vfcm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vfcm")
@classmethod
def check_vfcm(cls, v):
    if len(v) != 12 or not all(
        isinstance(x, (int, float)) and 0 <= x <= 1 for x in v
    ):
        raise ValueError("vfcm must be a list of 12 numbers between 0 and 1")
    return v

check_ivnc classmethod

check_ivnc(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ivnc")
@classmethod
def check_ivnc(cls, v):
    if v not in [0, 1]:
        raise ValueError("ivnc must be either 0 or 1")
    return v

check_ivpc classmethod

check_ivpc(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ivpc")
@classmethod
def check_ivpc(cls, v):
    if v not in [0, 1]:
        raise ValueError("ivpc must be either 0 or 1")
    return v

check_vkhns classmethod

check_vkhns(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vkhns")
@classmethod
def check_vkhns(cls, v):
    if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
        raise ValueError("vkhns must be a list of 3 positive numbers")
    return v

check_vkhps classmethod

check_vkhps(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vkhps")
@classmethod
def check_vkhps(cls, v):
    if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
        raise ValueError("vkhps must be a list of 3 positive numbers")
    return v

check_vscr classmethod

check_vscr(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vscr")
@classmethod
def check_vscr(cls, v):
    if len(v) != 3 or not all(
        isinstance(x, (int, float)) and 0 <= x <= 40 for x in v
    ):
        raise ValueError("vscr must be a list of 3 numbers between 0 and 40")
    return v

check_vsopt classmethod

check_vsopt(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vsopt")
@classmethod
def check_vsopt(cls, v):
    if len(v) != 3 or not all(
        isinstance(x, (int, float)) and 0 <= x <= 40 for x in v
    ):
        raise ValueError("vsopt must be a list of 3 numbers between 0 and 40")
    return v

check_vinun classmethod

check_vinun(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vinun")
@classmethod
def check_vinun(cls, v):
    if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
        raise ValueError("vinun must be a list of 3 positive numbers")
    return v

check_ivns classmethod

check_ivns(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ivns")
@classmethod
def check_ivns(cls, v):
    if v not in [0, 1]:
        raise ValueError("ivns must be either 0 or 1")
    return v

check_ivps classmethod

check_ivps(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ivps")
@classmethod
def check_ivps(cls, v):
    if v not in [0, 1]:
        raise ValueError("ivps must be either 0 or 1")
    return v

check_ivmrt classmethod

check_ivmrt(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ivmrt")
@classmethod
def check_ivmrt(cls, v):
    if v not in [0, 1]:
        raise ValueError("ivmrt must be either 0 or 1")
    return v

check_vtmr classmethod

check_vtmr(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vtmr")
@classmethod
def check_vtmr(cls, v):
    if len(v) != 6 or not all(
        isinstance(x, (int, float)) and 0 < x < 50 for x in v
    ):
        raise ValueError("vtmr must be a list of 6 numbers between 0 and 50")
    return v

check_vktmr classmethod

check_vktmr(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vktmr")
@classmethod
def check_vktmr(cls, v):
    if len(v) != 6 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
        raise ValueError("vktmr must be a list of 6 positive numbers")
    return v

check_vmr0 classmethod

check_vmr0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vmr0")
@classmethod
def check_vmr0(cls, v):
    if len(v) != 6 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
        raise ValueError("vmr0 must be a list of 6 positive numbers")
    return v

check_vmrcr classmethod

check_vmrcr(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vmrcr")
@classmethod
def check_vmrcr(cls, v):
    if len(v) != 6 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
        raise ValueError("vmrcr must be a list of 6 positive numbers")
    return v

check_valpha classmethod

check_valpha(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("valpha")
@classmethod
def check_valpha(cls, v):
    if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
        raise ValueError("valpha must be a list of 3 positive numbers")
    return v

check_vke classmethod

check_vke(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vke")
@classmethod
def check_vke(cls, v):
    if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
        raise ValueError("vke must be a list of 3 positive numbers")
    return v

check_vht0 classmethod

check_vht0(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vht0")
@classmethod
def check_vht0(cls, v):
    if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
        raise ValueError("vht0 must be a list of 3 positive numbers")
    return v

check_vcrit classmethod

check_vcrit(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vcrit")
@classmethod
def check_vcrit(cls, v):
    if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
        raise ValueError("vcrit must be a list of 3 positive numbers")
    return v

check_v2ht classmethod

check_v2ht(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("v2ht")
@classmethod
def check_v2ht(cls, v):
    if len(v) != 6 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
        raise ValueError("v2ht must be a list of 6 positive numbers")
    return v

check_vc2dw classmethod

check_vc2dw(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vc2dw")
@classmethod
def check_vc2dw(cls, v):
    if len(v) != 3 or not all(isinstance(x, (int, float)) and 0 < x < 1 for x in v):
        raise ValueError("vc2dw must be a list of 3 numbers between 0 and 1")
    return v

check_v2den classmethod

check_v2den(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("v2den")
@classmethod
def check_v2den(cls, v):
    if len(v) != 3 or not all(isinstance(x, int) and x > 0 for x in v):
        raise ValueError("v2den must be a list of 3 positive integers")
    return v

check_vp2c classmethod

check_vp2c(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vp2c")
@classmethod
def check_vp2c(cls, v):
    if len(v) != 3 or not all(isinstance(x, (int, float)) and 0 < x < 1 for x in v):
        raise ValueError("vp2c must be a list of 3 numbers between 0 and 1")
    return v

check_vn2c classmethod

check_vn2c(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vn2c")
@classmethod
def check_vn2c(cls, v):
    if len(v) != 3 or not all(isinstance(x, (int, float)) and 0 < x < 1 for x in v):
        raise ValueError("vn2c must be a list of 3 numbers between 0 and 1")
    return v

check_vo2c classmethod

check_vo2c(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("vo2c")
@classmethod
def check_vo2c(cls, v):
    if len(v) != 3 or not all(isinstance(x, (int, float)) and x > 0 for x in v):
        raise ValueError("vo2c must be a list of 3 positive numbers")
    return v

check_vfcp_sum

check_vfcp_sum()
Source code in rompy_schism/namelists/icm.py
@model_validator(mode="after")
def check_vfcp_sum(self):
    for i in range(3):
        if abs(sum(self.vfcp[i::3]) - 1) > 1e-6:
            raise ValueError(
                f"Sum of vfcp values for vegetation type {i+1} must be 1"
            )
    return self

check_vfnm_sum

check_vfnm_sum()
Source code in rompy_schism/namelists/icm.py
@model_validator(mode="after")
def check_vfnm_sum(self):
    for i in range(3):
        if abs(sum(self.vfnm[i::3]) - 1) > 1e-6:
            raise ValueError(
                f"Sum of vfnm values for vegetation type {i+1} must be 1"
            )
    return self

check_vfpm_sum

check_vfpm_sum()
Source code in rompy_schism/namelists/icm.py
@model_validator(mode="after")
def check_vfpm_sum(self):
    for i in range(3):
        if abs(sum(self.vfpm[i::3]) - 1) > 1e-6:
            raise ValueError(
                f"Sum of vfpm values for vegetation type {i+1} must be 1"
            )
    return self

check_vfcm_sum

check_vfcm_sum()
Source code in rompy_schism/namelists/icm.py
@model_validator(mode="after")
def check_vfcm_sum(self):
    for i in range(3):
        if abs(sum(self.vfcm[i::3]) - 1) > 1e-6:
            raise ValueError(
                f"Sum of vfcm values for vegetation type {i+1} must be 1"
            )
    return self

Zb

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/icm.py
class Zb(NamelistBaseModel):
    zgpm: Optional[list] = Field(
        [
            0.0,
            0.0,
            1.75,
            1.75,
            1.75,
            1.75,
            1.75,
            1.75,
            1.0,
            0.0,
            2.0,
            2.0,
            2.0,
            2.0,
            2.0,
            2.0,
        ],
        description="Zooplankton predation rate (day^-1) for different prey types and zooplankton groups. Dimension: (prey=1:8, ZB=1:2)",
    )
    zkhg: Optional[list] = Field(
        [
            0.175,
            0.175,
            0.175,
            0.175,
            0.175,
            0.175,
            0.175,
            0.175,
            0.175,
            0.175,
            0.175,
            0.175,
            0.175,
            0.175,
            0.175,
            0.175,
        ],
        description="Reference prey concentration (mg/L) for zooplankton growth. Dimension: (prey=1:8, ZB=1:2)",
    )
    ztgp: Optional[list] = Field(
        [25.0, 25.0], description="Optimal temperature for zooplankton growth (°C)"
    )
    zktgp: Optional[list] = Field(
        [0.0035, 0.008, 0.025, 0.03],
        description="Temperature dependence for zooplankton growth (°C^-2). Dimension: (ZB=1:2, 1:2) for T<=zTGP & T>zTGP",
    )
    zag: Optional[float] = Field(
        0.75, description="Zooplankton assimilation efficiency ratio (0-1)"
    )
    zrg: Optional[float] = Field(
        0.1, description="Zooplankton respiration ratio when grazing (0-1)"
    )
    zmrt: Optional[list] = Field(
        [0.02, 0.02], description="Zooplankton mortality rates (day^-1)"
    )
    zmtb: Optional[list] = Field(
        [0.254, 0.186], description="Zooplankton metabolism rates (day^-1)"
    )
    ztmt: Optional[list] = Field(
        [20.0, 20.0],
        description="Reference temperature for zooplankton metabolism (°C)",
    )
    zktmt: Optional[list] = Field(
        [0.0693, 0.0693],
        description="Temperature dependence for zooplankton metabolism (°C^-1)",
    )
    zfcp: Optional[list] = Field(
        [0.35, 0.55, 0.1],
        description="Fractions of zooplankton carbon partitioned into (RPOC, LPOC, DOC)",
    )
    zfnp: Optional[list] = Field(
        [0.35, 0.5, 0.1, 0.05],
        description="Fractions of zooplankton nitrogen partitioned into (RPON, LPON, DON, NH4)",
    )
    zfpp: Optional[list] = Field(
        [0.1, 0.2, 0.5, 0.2],
        description="Fractions of zooplankton phosphorus partitioned into (RPOP, LPOP, DOP, PO4)",
    )
    zfsp: Optional[list] = Field(
        [0.7, 0.25],
        description="Fractions of zooplankton silica partitioned into (SU, SA)",
    )
    zfcm: Optional[list] = Field(
        [0.1, 0.0],
        description="Fractions of zooplankton metabolism carbon partitioned into DOC. Dimension: (ZB=1:2)",
    )
    zfnm: Optional[list] = Field(
        [0.35, 0.3, 0.5, 0.4, 0.1, 0.2, 0.05, 0.1],
        description="Fractions of zooplankton metabolism nitrogen partitioned into (RPON, LPON, DON, NH4). Dimension: (ZB=1:2, 4)",
    )
    zfpm: Optional[list] = Field(
        [0.35, 0.3, 0.5, 0.4, 0.1, 0.2, 0.05, 0.1],
        description="Fractions of zooplankton metabolism phosphorus partitioned into (RPOP, LPOP, DOP, PO4). Dimension: (ZB=1:2, 4)",
    )
    zfsm: Optional[list] = Field(
        [0.5, 0.4, 0.5, 0.6],
        description="Fractions of zooplankton metabolism silica partitioned into (SU, SA). Dimension: (ZB=1:2, 2)",
    )
    zkhdo: Optional[list] = Field(
        [0.5, 0.5],
        description="Dissolved oxygen half-saturation for zooplankton's DOC excretion (mg/L)",
    )
    zn2c: Optional[list] = Field(
        [0.2, 0.2], description="Nitrogen to carbon ratio for zooplankton"
    )
    zp2c: Optional[list] = Field(
        [0.02, 0.02], description="Phosphorus to carbon ratio for zooplankton"
    )
    zs2c: Optional[list] = Field(
        [0.5, 0.5], description="Silica to carbon ratio for zooplankton"
    )
    z2pr: Optional[list] = Field(
        [0.5, 0.5],
        description="Ratio converting zooplankton and phytoplankton biomass to predation rates on zooplankton (L.mg^-1.day^-1)",
    )
    p2pr: Optional[float] = Field(
        0.25,
        description="Ratio converting zooplankton and phytoplankton biomass to predation rates on phytoplankton (L.mg^-1.day^-1)",
    )

    @field_validator("zgpm")
    @classmethod
    def validate_zgpm(cls, v):
        if not isinstance(v, list) or len(v) != 16:
            raise ValueError("zgpm must be a list of 16 float values")
        return [float(x) for x in v]

    @field_validator("zkhg")
    @classmethod
    def validate_zkhg(cls, v):
        if not isinstance(v, list) or len(v) != 16:
            raise ValueError("zkhg must be a list of 16 float values")
        return [float(x) for x in v]

    @field_validator("ztgp")
    @classmethod
    def validate_ztgp(cls, v):
        if not isinstance(v, list) or len(v) != 2:
            raise ValueError("ztgp must be a list of 2 float values")
        return [float(x) for x in v]

    @field_validator("zktgp")
    @classmethod
    def validate_zktgp(cls, v):
        if not isinstance(v, list) or len(v) != 4:
            raise ValueError("zktgp must be a list of 4 float values")
        return [float(x) for x in v]

    @field_validator("zag")
    @classmethod
    def validate_zag(cls, v):
        if not 0 <= float(v) <= 1:
            raise ValueError("zag must be between 0 and 1")
        return float(v)

    @field_validator("zrg")
    @classmethod
    def validate_zrg(cls, v):
        if not 0 <= float(v) <= 1:
            raise ValueError("zrg must be between 0 and 1")
        return float(v)

    @field_validator("zmrt")
    @classmethod
    def validate_zmrt(cls, v):
        if not isinstance(v, list) or len(v) != 2:
            raise ValueError("zmrt must be a list of 2 float values")
        return [float(x) for x in v]

    @field_validator("zmtb")
    @classmethod
    def validate_zmtb(cls, v):
        if not isinstance(v, list) or len(v) != 2:
            raise ValueError("zmtb must be a list of 2 float values")
        return [float(x) for x in v]

    @field_validator("ztmt")
    @classmethod
    def validate_ztmt(cls, v):
        if not isinstance(v, list) or len(v) != 2:
            raise ValueError("ztmt must be a list of 2 float values")
        return [float(x) for x in v]

    @field_validator("zktmt")
    @classmethod
    def validate_zktmt(cls, v):
        if not isinstance(v, list) or len(v) != 2:
            raise ValueError("zktmt must be a list of 2 float values")
        return [float(x) for x in v]

    @field_validator("zfcp")
    @classmethod
    def validate_zfcp(cls, v):
        if not isinstance(v, list) or len(v) != 3 or sum(v) != 1.0:
            raise ValueError("zfcp must be a list of 3 float values summing to 1.0")
        return [float(x) for x in v]

    @field_validator("zfnp")
    @classmethod
    def validate_zfnp(cls, v):
        if not isinstance(v, list) or len(v) != 4 or sum(v) != 1.0:
            raise ValueError("zfnp must be a list of 4 float values summing to 1.0")
        return [float(x) for x in v]

    @field_validator("zfpp")
    @classmethod
    def validate_zfpp(cls, v):
        if not isinstance(v, list) or len(v) != 4 or sum(v) != 1.0:
            raise ValueError("zfpp must be a list of 4 float values summing to 1.0")
        return [float(x) for x in v]

    @field_validator("zfsp")
    @classmethod
    def validate_zfsp(cls, v):
        if not isinstance(v, list) or len(v) != 2 or sum(v) != 1.0:
            raise ValueError("zfsp must be a list of 2 float values summing to 1.0")
        return [float(x) for x in v]

    @field_validator("zfcm")
    @classmethod
    def validate_zfcm(cls, v):
        if not isinstance(v, list) or len(v) != 2:
            raise ValueError("zfcm must be a list of 2 float values")
        return [float(x) for x in v]

    @field_validator("zfnm")
    @classmethod
    def validate_zfnm(cls, v):
        if not isinstance(v, list) or len(v) != 8:
            raise ValueError("zfnm must be a list of 8 float values")
        return [float(x) for x in v]

    @field_validator("zfpm")
    @classmethod
    def validate_zfpm(cls, v):
        if not isinstance(v, list) or len(v) != 8:
            raise ValueError("zfpm must be a list of 8 float values")
        return [float(x) for x in v]

    @field_validator("zfsm")
    @classmethod
    def validate_zfsm(cls, v):
        if not isinstance(v, list) or len(v) != 4:
            raise ValueError("zfsm must be a list of 4 float values")
        return [float(x) for x in v]

    @field_validator("zkhdo")
    @classmethod
    def validate_zkhdo(cls, v):
        if not isinstance(v, list) or len(v) != 2:
            raise ValueError("zkhdo must be a list of 2 float values")
        return [float(x) for x in v]

    @field_validator("zn2c")
    @classmethod
    def validate_zn2c(cls, v):
        if not isinstance(v, list) or len(v) != 2:
            raise ValueError("zn2c must be a list of 2 float values")
        return [float(x) for x in v]

    @field_validator("zp2c")
    @classmethod
    def validate_zp2c(cls, v):
        if not isinstance(v, list) or len(v) != 2:
            raise ValueError("zp2c must be a list of 2 float values")
        return [float(x) for x in v]

    @field_validator("zs2c")
    @classmethod
    def validate_zs2c(cls, v):
        if not isinstance(v, list) or len(v) != 2:
            raise ValueError("zs2c must be a list of 2 float values")
        return [float(x) for x in v]

    @field_validator("z2pr")
    @classmethod
    def validate_z2pr(cls, v):
        if not isinstance(v, list) or len(v) != 2:
            raise ValueError("z2pr must be a list of 2 float values")
        return [float(x) for x in v]

    @field_validator("p2pr")
    @classmethod
    def validate_p2pr(cls, v):
        return float(v)

Attributes

zgpm class-attribute instance-attribute

zgpm: Optional[list] = Field([0.0, 0.0, 1.75, 1.75, 1.75, 1.75, 1.75, 1.75, 1.0, 0.0, 2.0, 2.0, 2.0, 2.0, 2.0, 2.0], description='Zooplankton predation rate (day^-1) for different prey types and zooplankton groups. Dimension: (prey=1:8, ZB=1:2)')

zkhg class-attribute instance-attribute

zkhg: Optional[list] = Field([0.175, 0.175, 0.175, 0.175, 0.175, 0.175, 0.175, 0.175, 0.175, 0.175, 0.175, 0.175, 0.175, 0.175, 0.175, 0.175], description='Reference prey concentration (mg/L) for zooplankton growth. Dimension: (prey=1:8, ZB=1:2)')

ztgp class-attribute instance-attribute

ztgp: Optional[list] = Field([25.0, 25.0], description='Optimal temperature for zooplankton growth (°C)')

zktgp class-attribute instance-attribute

zktgp: Optional[list] = Field([0.0035, 0.008, 0.025, 0.03], description='Temperature dependence for zooplankton growth (°C^-2). Dimension: (ZB=1:2, 1:2) for T<=zTGP & T>zTGP')

zag class-attribute instance-attribute

zag: Optional[float] = Field(0.75, description='Zooplankton assimilation efficiency ratio (0-1)')

zrg class-attribute instance-attribute

zrg: Optional[float] = Field(0.1, description='Zooplankton respiration ratio when grazing (0-1)')

zmrt class-attribute instance-attribute

zmrt: Optional[list] = Field([0.02, 0.02], description='Zooplankton mortality rates (day^-1)')

zmtb class-attribute instance-attribute

zmtb: Optional[list] = Field([0.254, 0.186], description='Zooplankton metabolism rates (day^-1)')

ztmt class-attribute instance-attribute

ztmt: Optional[list] = Field([20.0, 20.0], description='Reference temperature for zooplankton metabolism (°C)')

zktmt class-attribute instance-attribute

zktmt: Optional[list] = Field([0.0693, 0.0693], description='Temperature dependence for zooplankton metabolism (°C^-1)')

zfcp class-attribute instance-attribute

zfcp: Optional[list] = Field([0.35, 0.55, 0.1], description='Fractions of zooplankton carbon partitioned into (RPOC, LPOC, DOC)')

zfnp class-attribute instance-attribute

zfnp: Optional[list] = Field([0.35, 0.5, 0.1, 0.05], description='Fractions of zooplankton nitrogen partitioned into (RPON, LPON, DON, NH4)')

zfpp class-attribute instance-attribute

zfpp: Optional[list] = Field([0.1, 0.2, 0.5, 0.2], description='Fractions of zooplankton phosphorus partitioned into (RPOP, LPOP, DOP, PO4)')

zfsp class-attribute instance-attribute

zfsp: Optional[list] = Field([0.7, 0.25], description='Fractions of zooplankton silica partitioned into (SU, SA)')

zfcm class-attribute instance-attribute

zfcm: Optional[list] = Field([0.1, 0.0], description='Fractions of zooplankton metabolism carbon partitioned into DOC. Dimension: (ZB=1:2)')

zfnm class-attribute instance-attribute

zfnm: Optional[list] = Field([0.35, 0.3, 0.5, 0.4, 0.1, 0.2, 0.05, 0.1], description='Fractions of zooplankton metabolism nitrogen partitioned into (RPON, LPON, DON, NH4). Dimension: (ZB=1:2, 4)')

zfpm class-attribute instance-attribute

zfpm: Optional[list] = Field([0.35, 0.3, 0.5, 0.4, 0.1, 0.2, 0.05, 0.1], description='Fractions of zooplankton metabolism phosphorus partitioned into (RPOP, LPOP, DOP, PO4). Dimension: (ZB=1:2, 4)')

zfsm class-attribute instance-attribute

zfsm: Optional[list] = Field([0.5, 0.4, 0.5, 0.6], description='Fractions of zooplankton metabolism silica partitioned into (SU, SA). Dimension: (ZB=1:2, 2)')

zkhdo class-attribute instance-attribute

zkhdo: Optional[list] = Field([0.5, 0.5], description="Dissolved oxygen half-saturation for zooplankton's DOC excretion (mg/L)")

zn2c class-attribute instance-attribute

zn2c: Optional[list] = Field([0.2, 0.2], description='Nitrogen to carbon ratio for zooplankton')

zp2c class-attribute instance-attribute

zp2c: Optional[list] = Field([0.02, 0.02], description='Phosphorus to carbon ratio for zooplankton')

zs2c class-attribute instance-attribute

zs2c: Optional[list] = Field([0.5, 0.5], description='Silica to carbon ratio for zooplankton')

z2pr class-attribute instance-attribute

z2pr: Optional[list] = Field([0.5, 0.5], description='Ratio converting zooplankton and phytoplankton biomass to predation rates on zooplankton (L.mg^-1.day^-1)')

p2pr class-attribute instance-attribute

p2pr: Optional[float] = Field(0.25, description='Ratio converting zooplankton and phytoplankton biomass to predation rates on phytoplankton (L.mg^-1.day^-1)')

Functions

validate_zgpm classmethod

validate_zgpm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("zgpm")
@classmethod
def validate_zgpm(cls, v):
    if not isinstance(v, list) or len(v) != 16:
        raise ValueError("zgpm must be a list of 16 float values")
    return [float(x) for x in v]

validate_zkhg classmethod

validate_zkhg(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("zkhg")
@classmethod
def validate_zkhg(cls, v):
    if not isinstance(v, list) or len(v) != 16:
        raise ValueError("zkhg must be a list of 16 float values")
    return [float(x) for x in v]

validate_ztgp classmethod

validate_ztgp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ztgp")
@classmethod
def validate_ztgp(cls, v):
    if not isinstance(v, list) or len(v) != 2:
        raise ValueError("ztgp must be a list of 2 float values")
    return [float(x) for x in v]

validate_zktgp classmethod

validate_zktgp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("zktgp")
@classmethod
def validate_zktgp(cls, v):
    if not isinstance(v, list) or len(v) != 4:
        raise ValueError("zktgp must be a list of 4 float values")
    return [float(x) for x in v]

validate_zag classmethod

validate_zag(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("zag")
@classmethod
def validate_zag(cls, v):
    if not 0 <= float(v) <= 1:
        raise ValueError("zag must be between 0 and 1")
    return float(v)

validate_zrg classmethod

validate_zrg(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("zrg")
@classmethod
def validate_zrg(cls, v):
    if not 0 <= float(v) <= 1:
        raise ValueError("zrg must be between 0 and 1")
    return float(v)

validate_zmrt classmethod

validate_zmrt(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("zmrt")
@classmethod
def validate_zmrt(cls, v):
    if not isinstance(v, list) or len(v) != 2:
        raise ValueError("zmrt must be a list of 2 float values")
    return [float(x) for x in v]

validate_zmtb classmethod

validate_zmtb(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("zmtb")
@classmethod
def validate_zmtb(cls, v):
    if not isinstance(v, list) or len(v) != 2:
        raise ValueError("zmtb must be a list of 2 float values")
    return [float(x) for x in v]

validate_ztmt classmethod

validate_ztmt(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("ztmt")
@classmethod
def validate_ztmt(cls, v):
    if not isinstance(v, list) or len(v) != 2:
        raise ValueError("ztmt must be a list of 2 float values")
    return [float(x) for x in v]

validate_zktmt classmethod

validate_zktmt(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("zktmt")
@classmethod
def validate_zktmt(cls, v):
    if not isinstance(v, list) or len(v) != 2:
        raise ValueError("zktmt must be a list of 2 float values")
    return [float(x) for x in v]

validate_zfcp classmethod

validate_zfcp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("zfcp")
@classmethod
def validate_zfcp(cls, v):
    if not isinstance(v, list) or len(v) != 3 or sum(v) != 1.0:
        raise ValueError("zfcp must be a list of 3 float values summing to 1.0")
    return [float(x) for x in v]

validate_zfnp classmethod

validate_zfnp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("zfnp")
@classmethod
def validate_zfnp(cls, v):
    if not isinstance(v, list) or len(v) != 4 or sum(v) != 1.0:
        raise ValueError("zfnp must be a list of 4 float values summing to 1.0")
    return [float(x) for x in v]

validate_zfpp classmethod

validate_zfpp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("zfpp")
@classmethod
def validate_zfpp(cls, v):
    if not isinstance(v, list) or len(v) != 4 or sum(v) != 1.0:
        raise ValueError("zfpp must be a list of 4 float values summing to 1.0")
    return [float(x) for x in v]

validate_zfsp classmethod

validate_zfsp(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("zfsp")
@classmethod
def validate_zfsp(cls, v):
    if not isinstance(v, list) or len(v) != 2 or sum(v) != 1.0:
        raise ValueError("zfsp must be a list of 2 float values summing to 1.0")
    return [float(x) for x in v]

validate_zfcm classmethod

validate_zfcm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("zfcm")
@classmethod
def validate_zfcm(cls, v):
    if not isinstance(v, list) or len(v) != 2:
        raise ValueError("zfcm must be a list of 2 float values")
    return [float(x) for x in v]

validate_zfnm classmethod

validate_zfnm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("zfnm")
@classmethod
def validate_zfnm(cls, v):
    if not isinstance(v, list) or len(v) != 8:
        raise ValueError("zfnm must be a list of 8 float values")
    return [float(x) for x in v]

validate_zfpm classmethod

validate_zfpm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("zfpm")
@classmethod
def validate_zfpm(cls, v):
    if not isinstance(v, list) or len(v) != 8:
        raise ValueError("zfpm must be a list of 8 float values")
    return [float(x) for x in v]

validate_zfsm classmethod

validate_zfsm(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("zfsm")
@classmethod
def validate_zfsm(cls, v):
    if not isinstance(v, list) or len(v) != 4:
        raise ValueError("zfsm must be a list of 4 float values")
    return [float(x) for x in v]

validate_zkhdo classmethod

validate_zkhdo(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("zkhdo")
@classmethod
def validate_zkhdo(cls, v):
    if not isinstance(v, list) or len(v) != 2:
        raise ValueError("zkhdo must be a list of 2 float values")
    return [float(x) for x in v]

validate_zn2c classmethod

validate_zn2c(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("zn2c")
@classmethod
def validate_zn2c(cls, v):
    if not isinstance(v, list) or len(v) != 2:
        raise ValueError("zn2c must be a list of 2 float values")
    return [float(x) for x in v]

validate_zp2c classmethod

validate_zp2c(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("zp2c")
@classmethod
def validate_zp2c(cls, v):
    if not isinstance(v, list) or len(v) != 2:
        raise ValueError("zp2c must be a list of 2 float values")
    return [float(x) for x in v]

validate_zs2c classmethod

validate_zs2c(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("zs2c")
@classmethod
def validate_zs2c(cls, v):
    if not isinstance(v, list) or len(v) != 2:
        raise ValueError("zs2c must be a list of 2 float values")
    return [float(x) for x in v]

validate_z2pr classmethod

validate_z2pr(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("z2pr")
@classmethod
def validate_z2pr(cls, v):
    if not isinstance(v, list) or len(v) != 2:
        raise ValueError("z2pr must be a list of 2 float values")
    return [float(x) for x in v]

validate_p2pr classmethod

validate_p2pr(v)
Source code in rompy_schism/namelists/icm.py
@field_validator("p2pr")
@classmethod
def validate_p2pr(cls, v):
    return float(v)

Icm

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/icm.py
class Icm(NamelistBaseModel):
    marco: Optional[Marco] = Field(default_factory=Marco)
    core: Optional[Core] = Field(default_factory=Core)
    sfm: Optional[Sfm] = Field(default_factory=Sfm)
    silica: Optional[Silica] = Field(default_factory=Silica)
    zb: Optional[Zb] = Field(default_factory=Zb)
    ph_icm: Optional[Ph_icm] = Field(default_factory=Ph_icm)
    sav: Optional[Sav] = Field(default_factory=Sav)
    stem: Optional[Stem] = Field(default_factory=Stem)
    veg: Optional[Veg] = Field(default_factory=Veg)
    bag: Optional[Bag] = Field(default_factory=Bag)
    ero: Optional[Ero] = Field(default_factory=Ero)
    poc: Optional[Poc] = Field(default_factory=Poc)

Attributes

marco class-attribute instance-attribute

marco: Optional[Marco] = Field(default_factory=Marco)

core class-attribute instance-attribute

core: Optional[Core] = Field(default_factory=Core)

sfm class-attribute instance-attribute

sfm: Optional[Sfm] = Field(default_factory=Sfm)

silica class-attribute instance-attribute

silica: Optional[Silica] = Field(default_factory=Silica)

zb class-attribute instance-attribute

zb: Optional[Zb] = Field(default_factory=Zb)

ph_icm class-attribute instance-attribute

ph_icm: Optional[Ph_icm] = Field(default_factory=Ph_icm)

sav class-attribute instance-attribute

sav: Optional[Sav] = Field(default_factory=Sav)

stem class-attribute instance-attribute

stem: Optional[Stem] = Field(default_factory=Stem)

veg class-attribute instance-attribute

veg: Optional[Veg] = Field(default_factory=Veg)

bag class-attribute instance-attribute

bag: Optional[Bag] = Field(default_factory=Bag)

ero class-attribute instance-attribute

ero: Optional[Ero] = Field(default_factory=Ero)

poc class-attribute instance-attribute

poc: Optional[Poc] = Field(default_factory=Poc)

SEDIMENT

Sed_opt

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/sediment.py
class Sed_opt(NamelistBaseModel):
    isedtype: Optional[list] = Field(
        [1, 1, 1, 1, 1],
        description="Sediment type for each class. 0: MUD-like (suspension only), 1: SAND-like (suspension + bedload), 2: GRAVEL-like (not available)",
    )
    srho: Optional[list] = Field(
        [2650.0, 2650.0, 2650.0, 2650.0, 2650.0],
        description="Sediment grain density (kg/m3) for each sediment class",
    )
    comp_ws: Optional[int] = Field(
        0,
        description="Flag to enable/disable computation of sediment settling velocity. 0: Disabled (user-defined), 1: Enabled (computed from SAND_SD50 and SAND_SRHO)",
    )
    comp_tauce: Optional[int] = Field(
        0,
        description="Flag to enable/disable computation of sediment critical shear stress. 0: Disabled (user-defined), 1: Enabled (computed from SAND_SD50 and SAND_SRHO)",
    )
    wsed: Optional[list] = Field(
        [1.06, 3.92, 5.43, 10.19, 28.65],
        description="Particle settling velocity (mm/s) for each sediment class",
    )
    tau_ce: Optional[list] = Field(
        [0.15, 0.17, 0.23, 0.3, 0.6],
        description="Critical shear stress for erosion (Pa) for each sediment class",
    )
    sed_debug: Optional[int] = Field(
        0,
        description="Debug flag. 0: silent, 1: output variables to outputs/nonfatal_*",
    )
    ised_dump: Optional[int] = Field(
        0, description="Dumping/dredging option. 0: no, 1: needs input sed_dump.in"
    )
    bedload: Optional[int] = Field(
        1,
        description="Bedload transport formula. 0: Disabled, 1: van Rijn (2007), 3: Soulsby and Damgaard (2005), 4: Wu and Lin (2014)",
    )
    bedload_filter: Optional[int] = Field(
        0,
        description="Flag to enable/disable diffusive filter for bedload fluxes. 0: Disabled, 1: Enabled",
    )
    bedload_limiter: Optional[int] = Field(
        0,
        description="Flag to enable/disable limiting of bedload flux components. 0: Disabled, 1: Enabled",
    )
    suspended_load: Optional[int] = Field(
        1,
        description="Flag to enable/disable suspended load transport. 0: Disabled, 1: Enabled",
    )
    iasym: Optional[int] = Field(0, description="")
    w_asym_max: Optional[float] = Field(0.4, description="")
    elfrink_filter: Optional[int] = Field(0, description="")
    ech_uorb: Optional[int] = Field(200, description="")
    bedload_acc: Optional[int] = Field(0, description="")
    bedload_acc_filter: Optional[int] = Field(0, description="")
    kacc_hoe: Optional[float] = Field(1.4e-4, description="")
    kacc_dub: Optional[float] = Field(6.31e-5, description="")
    thresh_acc_opt: Optional[int] = Field(2, description="")
    acrit: Optional[float] = Field(0.2, description="")
    tau_option: Optional[int] = Field(1, description="")
    tau_max: Optional[float] = Field(10.0, description="[Pa]")
    zstress: Optional[float] = Field(0.2, description="[m]; only used if tau_option/=1")
    ierosion: Optional[int] = Field(0, description="")
    slope_formulation: Optional[int] = Field(4, description="")
    alpha_bs: Optional[float] = Field(
        1.0, description="only used if slope_formulation=4"
    )
    alpha_bn: Optional[float] = Field(
        1.5, description="only used if slope_formulation=4"
    )
    ised_bc_bot: Optional[int] = Field(1, description="")
    alphd: Optional[float] = Field(1.0, description="")
    refht: Optional[float] = Field(0.75, description="suggested value: 0.75;")
    tbp: Optional[float] = Field(100.0, description="suggested value: 100;")
    im_pick_up: Optional[int] = Field(4, description="")
    sed_morph: Optional[int] = Field(0, description="")
    sed_morph_time: Optional[float] = Field(1.0, description="")
    morph_fac: Optional[float] = Field(1.0, description="for all classes")
    drag_formulation: Optional[int] = Field(1, description="")
    ddensed: Optional[int] = Field(0, description="")
    bedforms_rough: Optional[int] = Field(0, description="")
    iwave_ripple: Optional[int] = Field(1, description="")
    irough_bdld: Optional[int] = Field(1, description="")
    slope_avalanching: Optional[int] = Field(1, description="")
    dry_slope_cr: Optional[float] = Field(1.0, description="")
    wet_slope_cr: Optional[float] = Field(0.3, description="")
    bedmass_filter: Optional[int] = Field(0, description="")
    bedmass_threshold: Optional[float] = Field(0.025, description="")
    bdldiffu: Optional[float] = Field(0.5, description="")
    bedload_coeff: Optional[float] = Field(1.0, description="")
    cdb_min: Optional[float] = Field(1e-6, description="")
    cdb_max: Optional[float] = Field(0.01, description="")
    actv_max: Optional[float] = Field(0.05, description="")
    nbed: Optional[int] = Field(1, description="")
    sedlay_ini_opt: Optional[int] = Field(0, description="")
    toplay_inithick: Optional[float] = Field(0.1, description="")
    newlayer_thick: Optional[float] = Field(0.001, description="")
    imeth_bed_evol: Optional[int] = Field(2, description="")
    poro_option: Optional[int] = Field(1, description="")
    porosity: Optional[float] = Field(0.4, description="")
    awooster: Optional[float] = Field(0.42, description="")
    bwooster: Optional[float] = Field(-0.458, description="")

    @field_validator("isedtype")
    @classmethod
    def validate_isedtype(cls, v):
        if not all(0 <= x <= 1 for x in v):
            raise ValueError("isedtype values must be 0 or 1")
        return v

    @field_validator("srho")
    @classmethod
    def validate_srho(cls, v):
        if any(x <= 0 for x in v):
            raise ValueError("srho values must be positive")
        return v

    @field_validator("comp_ws")
    @classmethod
    def validate_comp_ws(cls, v):
        if v not in [0, 1]:
            raise ValueError("comp_ws must be 0 or 1")
        return v

    @field_validator("comp_tauce")
    @classmethod
    def validate_comp_tauce(cls, v):
        if v not in [0, 1]:
            raise ValueError("comp_tauce must be 0 or 1")
        return v

    @field_validator("wsed")
    @classmethod
    def validate_wsed(cls, v):
        if any(x < 0 for x in v):
            raise ValueError("wsed values must be non-negative")
        return v

    @field_validator("tau_ce")
    @classmethod
    def validate_tau_ce(cls, v):
        if any(x < 0 for x in v):
            raise ValueError("tau_ce values must be non-negative")
        return v

    @field_validator("sed_debug")
    @classmethod
    def validate_sed_debug(cls, v):
        if v not in [0, 1]:
            raise ValueError("sed_debug must be 0 or 1")
        return v

    @field_validator("ised_dump")
    @classmethod
    def validate_ised_dump(cls, v):
        if v not in [0, 1]:
            raise ValueError("ised_dump must be 0 or 1")
        return v

    @field_validator("bedload")
    @classmethod
    def validate_bedload(cls, v):
        if v not in [0, 1, 3, 4]:
            raise ValueError("bedload must be 0, 1, 3, or 4")
        return v

    @field_validator("bedload_filter")
    @classmethod
    def validate_bedload_filter(cls, v):
        if v not in [0, 1]:
            raise ValueError("bedload_filter must be 0 or 1")
        return v

    @field_validator("bedload_limiter")
    @classmethod
    def validate_bedload_limiter(cls, v):
        if v not in [0, 1]:
            raise ValueError("bedload_limiter must be 0 or 1")
        return v

    @field_validator("suspended_load")
    @classmethod
    def validate_suspended_load(cls, v):
        if v not in [0, 1]:
            raise ValueError("suspended_load must be 0 or 1")
        return v

    @model_validator(mode="after")
    def check_wsed_comp_ws(self):
        if self.comp_ws == 1 and any(
            self.isedtype[i] == 1 for i in range(len(self.isedtype))
        ):
            print(
                "Warning: wsed values will be overwritten for SAND-like classes when comp_ws=1"
            )
        return self

    @model_validator(mode="after")
    def check_tau_ce_comp_tauce(self):
        if self.comp_tauce == 1 and any(
            self.isedtype[i] == 1 for i in range(len(self.isedtype))
        ):
            print(
                "Warning: tau_ce values will be overwritten for SAND-like classes when comp_tauce=1"
            )
        return self

Attributes

isedtype class-attribute instance-attribute

isedtype: Optional[list] = Field([1, 1, 1, 1, 1], description='Sediment type for each class. 0: MUD-like (suspension only), 1: SAND-like (suspension + bedload), 2: GRAVEL-like (not available)')

srho class-attribute instance-attribute

srho: Optional[list] = Field([2650.0, 2650.0, 2650.0, 2650.0, 2650.0], description='Sediment grain density (kg/m3) for each sediment class')

comp_ws class-attribute instance-attribute

comp_ws: Optional[int] = Field(0, description='Flag to enable/disable computation of sediment settling velocity. 0: Disabled (user-defined), 1: Enabled (computed from SAND_SD50 and SAND_SRHO)')

comp_tauce class-attribute instance-attribute

comp_tauce: Optional[int] = Field(0, description='Flag to enable/disable computation of sediment critical shear stress. 0: Disabled (user-defined), 1: Enabled (computed from SAND_SD50 and SAND_SRHO)')

wsed class-attribute instance-attribute

wsed: Optional[list] = Field([1.06, 3.92, 5.43, 10.19, 28.65], description='Particle settling velocity (mm/s) for each sediment class')

tau_ce class-attribute instance-attribute

tau_ce: Optional[list] = Field([0.15, 0.17, 0.23, 0.3, 0.6], description='Critical shear stress for erosion (Pa) for each sediment class')

sed_debug class-attribute instance-attribute

sed_debug: Optional[int] = Field(0, description='Debug flag. 0: silent, 1: output variables to outputs/nonfatal_*')

ised_dump class-attribute instance-attribute

ised_dump: Optional[int] = Field(0, description='Dumping/dredging option. 0: no, 1: needs input sed_dump.in')

bedload class-attribute instance-attribute

bedload: Optional[int] = Field(1, description='Bedload transport formula. 0: Disabled, 1: van Rijn (2007), 3: Soulsby and Damgaard (2005), 4: Wu and Lin (2014)')

bedload_filter class-attribute instance-attribute

bedload_filter: Optional[int] = Field(0, description='Flag to enable/disable diffusive filter for bedload fluxes. 0: Disabled, 1: Enabled')

bedload_limiter class-attribute instance-attribute

bedload_limiter: Optional[int] = Field(0, description='Flag to enable/disable limiting of bedload flux components. 0: Disabled, 1: Enabled')

suspended_load class-attribute instance-attribute

suspended_load: Optional[int] = Field(1, description='Flag to enable/disable suspended load transport. 0: Disabled, 1: Enabled')

iasym class-attribute instance-attribute

iasym: Optional[int] = Field(0, description='')

w_asym_max class-attribute instance-attribute

w_asym_max: Optional[float] = Field(0.4, description='')

elfrink_filter class-attribute instance-attribute

elfrink_filter: Optional[int] = Field(0, description='')

ech_uorb class-attribute instance-attribute

ech_uorb: Optional[int] = Field(200, description='')

bedload_acc class-attribute instance-attribute

bedload_acc: Optional[int] = Field(0, description='')

bedload_acc_filter class-attribute instance-attribute

bedload_acc_filter: Optional[int] = Field(0, description='')

kacc_hoe class-attribute instance-attribute

kacc_hoe: Optional[float] = Field(0.00014, description='')

kacc_dub class-attribute instance-attribute

kacc_dub: Optional[float] = Field(6.31e-05, description='')

thresh_acc_opt class-attribute instance-attribute

thresh_acc_opt: Optional[int] = Field(2, description='')

acrit class-attribute instance-attribute

acrit: Optional[float] = Field(0.2, description='')

tau_option class-attribute instance-attribute

tau_option: Optional[int] = Field(1, description='')

tau_max class-attribute instance-attribute

tau_max: Optional[float] = Field(10.0, description='[Pa]')

zstress class-attribute instance-attribute

zstress: Optional[float] = Field(0.2, description='[m]; only used if tau_option/=1')

ierosion class-attribute instance-attribute

ierosion: Optional[int] = Field(0, description='')

slope_formulation class-attribute instance-attribute

slope_formulation: Optional[int] = Field(4, description='')

alpha_bs class-attribute instance-attribute

alpha_bs: Optional[float] = Field(1.0, description='only used if slope_formulation=4')

alpha_bn class-attribute instance-attribute

alpha_bn: Optional[float] = Field(1.5, description='only used if slope_formulation=4')

ised_bc_bot class-attribute instance-attribute

ised_bc_bot: Optional[int] = Field(1, description='')

alphd class-attribute instance-attribute

alphd: Optional[float] = Field(1.0, description='')

refht class-attribute instance-attribute

refht: Optional[float] = Field(0.75, description='suggested value: 0.75;')

tbp class-attribute instance-attribute

tbp: Optional[float] = Field(100.0, description='suggested value: 100;')

im_pick_up class-attribute instance-attribute

im_pick_up: Optional[int] = Field(4, description='')

sed_morph class-attribute instance-attribute

sed_morph: Optional[int] = Field(0, description='')

sed_morph_time class-attribute instance-attribute

sed_morph_time: Optional[float] = Field(1.0, description='')

morph_fac class-attribute instance-attribute

morph_fac: Optional[float] = Field(1.0, description='for all classes')

drag_formulation class-attribute instance-attribute

drag_formulation: Optional[int] = Field(1, description='')

ddensed class-attribute instance-attribute

ddensed: Optional[int] = Field(0, description='')

bedforms_rough class-attribute instance-attribute

bedforms_rough: Optional[int] = Field(0, description='')

iwave_ripple class-attribute instance-attribute

iwave_ripple: Optional[int] = Field(1, description='')

irough_bdld class-attribute instance-attribute

irough_bdld: Optional[int] = Field(1, description='')

slope_avalanching class-attribute instance-attribute

slope_avalanching: Optional[int] = Field(1, description='')

dry_slope_cr class-attribute instance-attribute

dry_slope_cr: Optional[float] = Field(1.0, description='')

wet_slope_cr class-attribute instance-attribute

wet_slope_cr: Optional[float] = Field(0.3, description='')

bedmass_filter class-attribute instance-attribute

bedmass_filter: Optional[int] = Field(0, description='')

bedmass_threshold class-attribute instance-attribute

bedmass_threshold: Optional[float] = Field(0.025, description='')

bdldiffu class-attribute instance-attribute

bdldiffu: Optional[float] = Field(0.5, description='')

bedload_coeff class-attribute instance-attribute

bedload_coeff: Optional[float] = Field(1.0, description='')

cdb_min class-attribute instance-attribute

cdb_min: Optional[float] = Field(1e-06, description='')

cdb_max class-attribute instance-attribute

cdb_max: Optional[float] = Field(0.01, description='')

actv_max class-attribute instance-attribute

actv_max: Optional[float] = Field(0.05, description='')

nbed class-attribute instance-attribute

nbed: Optional[int] = Field(1, description='')

sedlay_ini_opt class-attribute instance-attribute

sedlay_ini_opt: Optional[int] = Field(0, description='')

toplay_inithick class-attribute instance-attribute

toplay_inithick: Optional[float] = Field(0.1, description='')

newlayer_thick class-attribute instance-attribute

newlayer_thick: Optional[float] = Field(0.001, description='')

imeth_bed_evol class-attribute instance-attribute

imeth_bed_evol: Optional[int] = Field(2, description='')

poro_option class-attribute instance-attribute

poro_option: Optional[int] = Field(1, description='')

porosity class-attribute instance-attribute

porosity: Optional[float] = Field(0.4, description='')

awooster class-attribute instance-attribute

awooster: Optional[float] = Field(0.42, description='')

bwooster class-attribute instance-attribute

bwooster: Optional[float] = Field(-0.458, description='')

Functions

validate_isedtype classmethod

validate_isedtype(v)
Source code in rompy_schism/namelists/sediment.py
@field_validator("isedtype")
@classmethod
def validate_isedtype(cls, v):
    if not all(0 <= x <= 1 for x in v):
        raise ValueError("isedtype values must be 0 or 1")
    return v

validate_srho classmethod

validate_srho(v)
Source code in rompy_schism/namelists/sediment.py
@field_validator("srho")
@classmethod
def validate_srho(cls, v):
    if any(x <= 0 for x in v):
        raise ValueError("srho values must be positive")
    return v

validate_comp_ws classmethod

validate_comp_ws(v)
Source code in rompy_schism/namelists/sediment.py
@field_validator("comp_ws")
@classmethod
def validate_comp_ws(cls, v):
    if v not in [0, 1]:
        raise ValueError("comp_ws must be 0 or 1")
    return v

validate_comp_tauce classmethod

validate_comp_tauce(v)
Source code in rompy_schism/namelists/sediment.py
@field_validator("comp_tauce")
@classmethod
def validate_comp_tauce(cls, v):
    if v not in [0, 1]:
        raise ValueError("comp_tauce must be 0 or 1")
    return v

validate_wsed classmethod

validate_wsed(v)
Source code in rompy_schism/namelists/sediment.py
@field_validator("wsed")
@classmethod
def validate_wsed(cls, v):
    if any(x < 0 for x in v):
        raise ValueError("wsed values must be non-negative")
    return v

validate_tau_ce classmethod

validate_tau_ce(v)
Source code in rompy_schism/namelists/sediment.py
@field_validator("tau_ce")
@classmethod
def validate_tau_ce(cls, v):
    if any(x < 0 for x in v):
        raise ValueError("tau_ce values must be non-negative")
    return v

validate_sed_debug classmethod

validate_sed_debug(v)
Source code in rompy_schism/namelists/sediment.py
@field_validator("sed_debug")
@classmethod
def validate_sed_debug(cls, v):
    if v not in [0, 1]:
        raise ValueError("sed_debug must be 0 or 1")
    return v

validate_ised_dump classmethod

validate_ised_dump(v)
Source code in rompy_schism/namelists/sediment.py
@field_validator("ised_dump")
@classmethod
def validate_ised_dump(cls, v):
    if v not in [0, 1]:
        raise ValueError("ised_dump must be 0 or 1")
    return v

validate_bedload classmethod

validate_bedload(v)
Source code in rompy_schism/namelists/sediment.py
@field_validator("bedload")
@classmethod
def validate_bedload(cls, v):
    if v not in [0, 1, 3, 4]:
        raise ValueError("bedload must be 0, 1, 3, or 4")
    return v

validate_bedload_filter classmethod

validate_bedload_filter(v)
Source code in rompy_schism/namelists/sediment.py
@field_validator("bedload_filter")
@classmethod
def validate_bedload_filter(cls, v):
    if v not in [0, 1]:
        raise ValueError("bedload_filter must be 0 or 1")
    return v

validate_bedload_limiter classmethod

validate_bedload_limiter(v)
Source code in rompy_schism/namelists/sediment.py
@field_validator("bedload_limiter")
@classmethod
def validate_bedload_limiter(cls, v):
    if v not in [0, 1]:
        raise ValueError("bedload_limiter must be 0 or 1")
    return v

validate_suspended_load classmethod

validate_suspended_load(v)
Source code in rompy_schism/namelists/sediment.py
@field_validator("suspended_load")
@classmethod
def validate_suspended_load(cls, v):
    if v not in [0, 1]:
        raise ValueError("suspended_load must be 0 or 1")
    return v

check_wsed_comp_ws

check_wsed_comp_ws()
Source code in rompy_schism/namelists/sediment.py
@model_validator(mode="after")
def check_wsed_comp_ws(self):
    if self.comp_ws == 1 and any(
        self.isedtype[i] == 1 for i in range(len(self.isedtype))
    ):
        print(
            "Warning: wsed values will be overwritten for SAND-like classes when comp_ws=1"
        )
    return self

check_tau_ce_comp_tauce

check_tau_ce_comp_tauce()
Source code in rompy_schism/namelists/sediment.py
@model_validator(mode="after")
def check_tau_ce_comp_tauce(self):
    if self.comp_tauce == 1 and any(
        self.isedtype[i] == 1 for i in range(len(self.isedtype))
    ):
        print(
            "Warning: tau_ce values will be overwritten for SAND-like classes when comp_tauce=1"
        )
    return self

Sed_core

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/sediment.py
class Sed_core(NamelistBaseModel):
    sd50: Optional[list] = Field(
        [0.12, 0.18, 0.39, 0.60, 1.2],
        description="Median sediment grain diameter (D50) for each sediment tracer, specified in millimeters. This is a list of values corresponding to the number of sediment tracers (Ntracers).",
    )
    erate: Optional[list] = Field(
        [1.6e-3, 1.6e-3, 1.6e-3, 1.6e-3, 1.6e-3],
        description="Surface erosion rate for each sediment tracer. The interpretation and units depend on the 'ierosion' parameter. If ierosion=0, the units are kg/m²/s. If ierosion=1, the units are s/m (as per M_E in Winterwerp et al. 2012, JGR, vol 117).",
    )

    @field_validator("sd50")
    @classmethod
    def validate_sd50(cls, v):
        if not all(isinstance(x, (int, float)) and x > 0 for x in v):
            raise ValueError("All Sd50 values must be positive numbers")
        return v

    @field_validator("erate")
    @classmethod
    def validate_erate(cls, v):
        if not all(isinstance(x, (int, float)) and x >= 0 for x in v):
            raise ValueError("All Erate values must be non-negative numbers")
        return v

    @model_validator(mode="after")
    def validate_erate_length(self):
        if len(self.erate) != len(self.sd50):
            raise ValueError("Erate and Sd50 must have the same number of elements")
        return self

Attributes

sd50 class-attribute instance-attribute

sd50: Optional[list] = Field([0.12, 0.18, 0.39, 0.6, 1.2], description='Median sediment grain diameter (D50) for each sediment tracer, specified in millimeters. This is a list of values corresponding to the number of sediment tracers (Ntracers).')

erate class-attribute instance-attribute

erate: Optional[list] = Field([0.0016, 0.0016, 0.0016, 0.0016, 0.0016], description="Surface erosion rate for each sediment tracer. The interpretation and units depend on the 'ierosion' parameter. If ierosion=0, the units are kg/m²/s. If ierosion=1, the units are s/m (as per M_E in Winterwerp et al. 2012, JGR, vol 117).")

Functions

validate_sd50 classmethod

validate_sd50(v)
Source code in rompy_schism/namelists/sediment.py
@field_validator("sd50")
@classmethod
def validate_sd50(cls, v):
    if not all(isinstance(x, (int, float)) and x > 0 for x in v):
        raise ValueError("All Sd50 values must be positive numbers")
    return v

validate_erate classmethod

validate_erate(v)
Source code in rompy_schism/namelists/sediment.py
@field_validator("erate")
@classmethod
def validate_erate(cls, v):
    if not all(isinstance(x, (int, float)) and x >= 0 for x in v):
        raise ValueError("All Erate values must be non-negative numbers")
    return v

validate_erate_length

validate_erate_length()
Source code in rompy_schism/namelists/sediment.py
@model_validator(mode="after")
def validate_erate_length(self):
    if len(self.erate) != len(self.sd50):
        raise ValueError("Erate and Sd50 must have the same number of elements")
    return self

Sediment

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/sediment.py
class Sediment(NamelistBaseModel):
    sed_core: Optional[Sed_core] = Field(default_factory=Sed_core)
    sed_opt: Optional[Sed_opt] = Field(default_factory=Sed_opt)

Attributes

sed_core class-attribute instance-attribute

sed_core: Optional[Sed_core] = Field(default_factory=Sed_core)

sed_opt class-attribute instance-attribute

sed_opt: Optional[Sed_opt] = Field(default_factory=Sed_opt)

COSINE

Core

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/cosine.py
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
class Core(NamelistBaseModel):
    gmaxs: Optional[list] = Field(
        [2.0, 2.5], description="Maximum growth rates for two phytoplankton types"
    )
    gammas: Optional[list] = Field(
        [0.2, 0.075], description="Mortality rates for two phytoplankton types"
    )
    pis: Optional[list] = Field(
        [1.5, 1.5],
        description="Ammonium inhibition parameters for two phytoplankton types",
    )
    kno3s: Optional[list] = Field(
        [1.0, 3.0],
        description="NO3 half-saturation constants for two phytoplankton types",
    )
    knh4s: Optional[list] = Field(
        [0.15, 0.45],
        description="NH4 half-saturation constants for two phytoplankton types",
    )
    kpo4s: Optional[list] = Field(
        [0.1, 0.1],
        description="PO4 half-saturation constants for two phytoplankton types",
    )
    kco2s: Optional[list] = Field(
        [50.0, 50.0],
        description="CO2 half-saturation constants for two phytoplankton types",
    )
    ksio4: Optional[float] = Field(
        4.5, description="SiO4 half-saturation constant for diatoms"
    )
    kns: Optional[list] = Field(
        [0.0, 0.0],
        description="Nighttime uptake rates of NH4 for two phytoplankton types",
    )
    alphas: Optional[list] = Field(
        [0.1, 0.1],
        description="Initial slopes of P-I curve for two phytoplankton types",
    )
    betas: Optional[list] = Field(
        [0.0, 0.0],
        description="Slopes for photo-inhibition for two phytoplankton types",
    )
    aks: Optional[list] = Field(
        [0.75, 0.03, 0.066],
        description="Light extinction coefficients for phytoplankton and suspended particulate matter",
    )
    betaz: Optional[list] = Field(
        [1.35, 0.4], description="Maximum grazing rates for two zooplankton types"
    )
    alphaz: Optional[list] = Field(
        [0.75, 0.75], description="Assimilation rates for two zooplankton types"
    )
    gammaz: Optional[list] = Field(
        [0.2, 0.2], description="Mortality rates for two zooplankton types"
    )
    kez: Optional[list] = Field(
        [0.2, 0.2], description="Excretion rates for two zooplankton types"
    )
    kgz: Optional[list] = Field(
        [0.5, 0.25],
        description="Reference prey concentrations for grazing for two zooplankton types",
    )
    rhoz: Optional[list] = Field(
        [0.6, 0.3, 0.1], description="Prey preference factors of Z2 on (S2, Z1, DN)"
    )
    ipo4: Optional[int] = Field(
        1, description="Flag to add additional PO4 from biogenic silica dissolution"
    )
    tr: Optional[float] = Field(
        20.0,
        description="Reference temperature for temperature adjustment for CoSiNE sink and source",
    )
    kox: Optional[float] = Field(
        30.0, description="Reference oxygen concentration for oxidation"
    )
    wss2: Optional[float] = Field(
        0.2, description="Settling velocity of S2 (phytoplankton type 2)"
    )
    wsdn: Optional[float] = Field(
        1.0, description="Settling velocity of DN (detrital nitrogen)"
    )
    wsdsi: Optional[float] = Field(
        1.0, description="Settling velocity of DSi (dissolved silica)"
    )
    si2n: Optional[float] = Field(
        1.2, description="Silica to nitrogen conversion coefficient"
    )
    p2n: Optional[float] = Field(
        0.0625, description="Phosphorus to nitrogen conversion coefficient"
    )
    o2no: Optional[float] = Field(
        8.625, description="Oxygen to nitrogen (NO3) conversion coefficient"
    )
    o2nh: Optional[float] = Field(
        6.625, description="Oxygen to nitrogen (NH4) conversion coefficient"
    )
    c2n: Optional[float] = Field(
        7.3, description="Carbon to nitrogen conversion coefficient"
    )
    gamman: Optional[float] = Field(0.07, description="Nitrification coefficient")
    pco2a: Optional[float] = Field(391.63, description="Atmospheric CO2 concentration")
    kmdn: Optional[list] = Field(
        [0.009, 0.075],
        description="Remineralization coefficients for DN (detrital nitrogen)",
    )
    kmdsi: Optional[list] = Field(
        [0.0114, 0.015],
        description="Remineralization coefficients for DSi (dissolved silica)",
    )

    @field_validator("gmaxs")
    @classmethod
    def check_gmaxs(cls, v):
        if not (
            isinstance(v, list)
            and len(v) == 2
            and all(isinstance(x, float) and x > 0 for x in v)
        ):
            raise ValueError("gmaxs must be a list of 2 positive floats")
        return v

    @field_validator("gammas")
    @classmethod
    def check_gammas(cls, v):
        if not (
            isinstance(v, list)
            and len(v) == 2
            and all(isinstance(x, float) and 0 <= x <= 1 for x in v)
        ):
            raise ValueError("gammas must be a list of 2 floats between 0 and 1")
        return v

    @field_validator("pis")
    @classmethod
    def check_pis(cls, v):
        if not (
            isinstance(v, list)
            and len(v) == 2
            and all(isinstance(x, float) and x > 0 for x in v)
        ):
            raise ValueError("pis must be a list of 2 positive floats")
        return v

    @field_validator("kno3s")
    @classmethod
    def check_kno3s(cls, v):
        if not (
            isinstance(v, list)
            and len(v) == 2
            and all(isinstance(x, float) and x > 0 for x in v)
        ):
            raise ValueError("kno3s must be a list of 2 positive floats")
        return v

    @field_validator("knh4s")
    @classmethod
    def check_knh4s(cls, v):
        if not (
            isinstance(v, list)
            and len(v) == 2
            and all(isinstance(x, float) and x > 0 for x in v)
        ):
            raise ValueError("knh4s must be a list of 2 positive floats")
        return v

    @field_validator("kpo4s")
    @classmethod
    def check_kpo4s(cls, v):
        if not (
            isinstance(v, list)
            and len(v) == 2
            and all(isinstance(x, float) and x > 0 for x in v)
        ):
            raise ValueError("kpo4s must be a list of 2 positive floats")
        return v

    @field_validator("kco2s")
    @classmethod
    def check_kco2s(cls, v):
        if not (
            isinstance(v, list)
            and len(v) == 2
            and all(isinstance(x, float) and x > 0 for x in v)
        ):
            raise ValueError("kco2s must be a list of 2 positive floats")
        return v

    @field_validator("ksio4")
    @classmethod
    def check_ksio4(cls, v):
        if not (isinstance(v, float) and v > 0):
            raise ValueError("ksio4 must be a positive float")
        return v

    @field_validator("kns")
    @classmethod
    def check_kns(cls, v):
        if not (
            isinstance(v, list)
            and len(v) == 2
            and all(isinstance(x, float) and x >= 0 for x in v)
        ):
            raise ValueError("kns must be a list of 2 non-negative floats")
        return v

    @field_validator("alphas")
    @classmethod
    def check_alphas(cls, v):
        if not (
            isinstance(v, list)
            and len(v) == 2
            and all(isinstance(x, float) and x > 0 for x in v)
        ):
            raise ValueError("alphas must be a list of 2 positive floats")
        return v

    @field_validator("betas")
    @classmethod
    def check_betas(cls, v):
        if not (
            isinstance(v, list)
            and len(v) == 2
            and all(isinstance(x, float) and x >= 0 for x in v)
        ):
            raise ValueError("betas must be a list of 2 non-negative floats")
        return v

    @field_validator("aks")
    @classmethod
    def check_aks(cls, v):
        if not (
            isinstance(v, list)
            and len(v) == 3
            and all(isinstance(x, float) and x > 0 for x in v)
        ):
            raise ValueError("aks must be a list of 3 positive floats")
        return v

    @field_validator("betaz")
    @classmethod
    def check_betaz(cls, v):
        if not (
            isinstance(v, list)
            and len(v) == 2
            and all(isinstance(x, float) and x > 0 for x in v)
        ):
            raise ValueError("betaz must be a list of 2 positive floats")
        return v

    @field_validator("alphaz")
    @classmethod
    def check_alphaz(cls, v):
        if not (
            isinstance(v, list)
            and len(v) == 2
            and all(isinstance(x, float) and 0 < x <= 1 for x in v)
        ):
            raise ValueError("alphaz must be a list of 2 floats between 0 and 1")
        return v

    @field_validator("gammaz")
    @classmethod
    def check_gammaz(cls, v):
        if not (
            isinstance(v, list)
            and len(v) == 2
            and all(isinstance(x, float) and 0 < x <= 1 for x in v)
        ):
            raise ValueError("gammaz must be a list of 2 floats between 0 and 1")
        return v

    @field_validator("kez")
    @classmethod
    def check_kez(cls, v):
        if not (
            isinstance(v, list)
            and len(v) == 2
            and all(isinstance(x, float) and 0 < x <= 1 for x in v)
        ):
            raise ValueError("kez must be a list of 2 floats between 0 and 1")
        return v

    @field_validator("kgz")
    @classmethod
    def check_kgz(cls, v):
        if not (
            isinstance(v, list)
            and len(v) == 2
            and all(isinstance(x, float) and x > 0 for x in v)
        ):
            raise ValueError("kgz must be a list of 2 positive floats")
        return v

    @field_validator("rhoz")
    @classmethod
    def check_rhoz(cls, v):
        if not (
            isinstance(v, list)
            and len(v) == 3
            and all(isinstance(x, float) and 0 <= x <= 1 for x in v)
        ):
            raise ValueError("rhoz must be a list of 3 floats between 0 and 1")
        return v

    @field_validator("ipo4")
    @classmethod
    def check_ipo4(cls, v):
        if v not in [0, 1]:
            raise ValueError("ipo4 must be either 0 or 1")
        return v

    @field_validator("tr")
    @classmethod
    def check_tr(cls, v):
        if not (isinstance(v, float) and v > 0):
            raise ValueError("tr must be a positive float")
        return v

    @field_validator("kox")
    @classmethod
    def check_kox(cls, v):
        if not (isinstance(v, float) and v > 0):
            raise ValueError("kox must be a positive float")
        return v

    @field_validator("wss2")
    @classmethod
    def check_wss2(cls, v):
        if not (isinstance(v, float) and v >= 0):
            raise ValueError("wss2 must be a non-negative float")
        return v

    @field_validator("wsdn")
    @classmethod
    def check_wsdn(cls, v):
        if not (isinstance(v, float) and v >= 0):
            raise ValueError("wsdn must be a non-negative float")
        return v

    @field_validator("wsdsi")
    @classmethod
    def check_wsdsi(cls, v):
        if not (isinstance(v, float) and v >= 0):
            raise ValueError("wsdsi must be a non-negative float")
        return v

    @field_validator("si2n")
    @classmethod
    def check_si2n(cls, v):
        if not (isinstance(v, float) and v > 0):
            raise ValueError("si2n must be a positive float")
        return v

    @field_validator("p2n")
    @classmethod
    def check_p2n(cls, v):
        if not (isinstance(v, float) and v > 0):
            raise ValueError("p2n must be a positive float")
        return v

    @field_validator("o2no")
    @classmethod
    def check_o2no(cls, v):
        if not (isinstance(v, float) and v > 0):
            raise ValueError("o2no must be a positive float")
        return v

    @field_validator("o2nh")
    @classmethod
    def check_o2nh(cls, v):
        if not (isinstance(v, float) and v > 0):
            raise ValueError("o2nh must be a positive float")
        return v

    @field_validator("c2n")
    @classmethod
    def check_c2n(cls, v):
        if not (isinstance(v, float) and v > 0):
            raise ValueError("c2n must be a positive float")
        return v

    @field_validator("gamman")
    @classmethod
    def check_gamman(cls, v):
        if not (isinstance(v, float) and 0 <= v <= 1):
            raise ValueError("gamman must be a float between 0 and 1")
        return v

    @field_validator("pco2a")
    @classmethod
    def check_pco2a(cls, v):
        if not (isinstance(v, float) and v > 0):
            raise ValueError("pco2a must be a positive float")
        return v

    @field_validator("kmdn")
    @classmethod
    def check_kmdn(cls, v):
        if not (
            isinstance(v, list)
            and len(v) == 2
            and all(isinstance(x, float) and x >= 0 for x in v)
        ):
            raise ValueError("kmdn must be a list of 2 non-negative floats")
        return v

    @field_validator("kmdsi")
    @classmethod
    def check_kmdsi(cls, v):
        if not (
            isinstance(v, list)
            and len(v) == 2
            and all(isinstance(x, float) and x >= 0 for x in v)
        ):
            raise ValueError("kmdsi must be a list of 2 non-negative floats")
        return v

    @model_validator(mode="after")
    def check_rhoz_sum(self):
        if sum(self.rhoz) != 1:
            raise ValueError("Sum of rhoz values must equal 1")
        return self

Attributes

gmaxs class-attribute instance-attribute

gmaxs: Optional[list] = Field([2.0, 2.5], description='Maximum growth rates for two phytoplankton types')

gammas class-attribute instance-attribute

gammas: Optional[list] = Field([0.2, 0.075], description='Mortality rates for two phytoplankton types')

pis class-attribute instance-attribute

pis: Optional[list] = Field([1.5, 1.5], description='Ammonium inhibition parameters for two phytoplankton types')

kno3s class-attribute instance-attribute

kno3s: Optional[list] = Field([1.0, 3.0], description='NO3 half-saturation constants for two phytoplankton types')

knh4s class-attribute instance-attribute

knh4s: Optional[list] = Field([0.15, 0.45], description='NH4 half-saturation constants for two phytoplankton types')

kpo4s class-attribute instance-attribute

kpo4s: Optional[list] = Field([0.1, 0.1], description='PO4 half-saturation constants for two phytoplankton types')

kco2s class-attribute instance-attribute

kco2s: Optional[list] = Field([50.0, 50.0], description='CO2 half-saturation constants for two phytoplankton types')

ksio4 class-attribute instance-attribute

ksio4: Optional[float] = Field(4.5, description='SiO4 half-saturation constant for diatoms')

kns class-attribute instance-attribute

kns: Optional[list] = Field([0.0, 0.0], description='Nighttime uptake rates of NH4 for two phytoplankton types')

alphas class-attribute instance-attribute

alphas: Optional[list] = Field([0.1, 0.1], description='Initial slopes of P-I curve for two phytoplankton types')

betas class-attribute instance-attribute

betas: Optional[list] = Field([0.0, 0.0], description='Slopes for photo-inhibition for two phytoplankton types')

aks class-attribute instance-attribute

aks: Optional[list] = Field([0.75, 0.03, 0.066], description='Light extinction coefficients for phytoplankton and suspended particulate matter')

betaz class-attribute instance-attribute

betaz: Optional[list] = Field([1.35, 0.4], description='Maximum grazing rates for two zooplankton types')

alphaz class-attribute instance-attribute

alphaz: Optional[list] = Field([0.75, 0.75], description='Assimilation rates for two zooplankton types')

gammaz class-attribute instance-attribute

gammaz: Optional[list] = Field([0.2, 0.2], description='Mortality rates for two zooplankton types')

kez class-attribute instance-attribute

kez: Optional[list] = Field([0.2, 0.2], description='Excretion rates for two zooplankton types')

kgz class-attribute instance-attribute

kgz: Optional[list] = Field([0.5, 0.25], description='Reference prey concentrations for grazing for two zooplankton types')

rhoz class-attribute instance-attribute

rhoz: Optional[list] = Field([0.6, 0.3, 0.1], description='Prey preference factors of Z2 on (S2, Z1, DN)')

ipo4 class-attribute instance-attribute

ipo4: Optional[int] = Field(1, description='Flag to add additional PO4 from biogenic silica dissolution')

tr class-attribute instance-attribute

tr: Optional[float] = Field(20.0, description='Reference temperature for temperature adjustment for CoSiNE sink and source')

kox class-attribute instance-attribute

kox: Optional[float] = Field(30.0, description='Reference oxygen concentration for oxidation')

wss2 class-attribute instance-attribute

wss2: Optional[float] = Field(0.2, description='Settling velocity of S2 (phytoplankton type 2)')

wsdn class-attribute instance-attribute

wsdn: Optional[float] = Field(1.0, description='Settling velocity of DN (detrital nitrogen)')

wsdsi class-attribute instance-attribute

wsdsi: Optional[float] = Field(1.0, description='Settling velocity of DSi (dissolved silica)')

si2n class-attribute instance-attribute

si2n: Optional[float] = Field(1.2, description='Silica to nitrogen conversion coefficient')

p2n class-attribute instance-attribute

p2n: Optional[float] = Field(0.0625, description='Phosphorus to nitrogen conversion coefficient')

o2no class-attribute instance-attribute

o2no: Optional[float] = Field(8.625, description='Oxygen to nitrogen (NO3) conversion coefficient')

o2nh class-attribute instance-attribute

o2nh: Optional[float] = Field(6.625, description='Oxygen to nitrogen (NH4) conversion coefficient')

c2n class-attribute instance-attribute

c2n: Optional[float] = Field(7.3, description='Carbon to nitrogen conversion coefficient')

gamman class-attribute instance-attribute

gamman: Optional[float] = Field(0.07, description='Nitrification coefficient')

pco2a class-attribute instance-attribute

pco2a: Optional[float] = Field(391.63, description='Atmospheric CO2 concentration')

kmdn class-attribute instance-attribute

kmdn: Optional[list] = Field([0.009, 0.075], description='Remineralization coefficients for DN (detrital nitrogen)')

kmdsi class-attribute instance-attribute

kmdsi: Optional[list] = Field([0.0114, 0.015], description='Remineralization coefficients for DSi (dissolved silica)')

Functions

check_gmaxs classmethod

check_gmaxs(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("gmaxs")
@classmethod
def check_gmaxs(cls, v):
    if not (
        isinstance(v, list)
        and len(v) == 2
        and all(isinstance(x, float) and x > 0 for x in v)
    ):
        raise ValueError("gmaxs must be a list of 2 positive floats")
    return v

check_gammas classmethod

check_gammas(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("gammas")
@classmethod
def check_gammas(cls, v):
    if not (
        isinstance(v, list)
        and len(v) == 2
        and all(isinstance(x, float) and 0 <= x <= 1 for x in v)
    ):
        raise ValueError("gammas must be a list of 2 floats between 0 and 1")
    return v

check_pis classmethod

check_pis(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("pis")
@classmethod
def check_pis(cls, v):
    if not (
        isinstance(v, list)
        and len(v) == 2
        and all(isinstance(x, float) and x > 0 for x in v)
    ):
        raise ValueError("pis must be a list of 2 positive floats")
    return v

check_kno3s classmethod

check_kno3s(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("kno3s")
@classmethod
def check_kno3s(cls, v):
    if not (
        isinstance(v, list)
        and len(v) == 2
        and all(isinstance(x, float) and x > 0 for x in v)
    ):
        raise ValueError("kno3s must be a list of 2 positive floats")
    return v

check_knh4s classmethod

check_knh4s(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("knh4s")
@classmethod
def check_knh4s(cls, v):
    if not (
        isinstance(v, list)
        and len(v) == 2
        and all(isinstance(x, float) and x > 0 for x in v)
    ):
        raise ValueError("knh4s must be a list of 2 positive floats")
    return v

check_kpo4s classmethod

check_kpo4s(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("kpo4s")
@classmethod
def check_kpo4s(cls, v):
    if not (
        isinstance(v, list)
        and len(v) == 2
        and all(isinstance(x, float) and x > 0 for x in v)
    ):
        raise ValueError("kpo4s must be a list of 2 positive floats")
    return v

check_kco2s classmethod

check_kco2s(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("kco2s")
@classmethod
def check_kco2s(cls, v):
    if not (
        isinstance(v, list)
        and len(v) == 2
        and all(isinstance(x, float) and x > 0 for x in v)
    ):
        raise ValueError("kco2s must be a list of 2 positive floats")
    return v

check_ksio4 classmethod

check_ksio4(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("ksio4")
@classmethod
def check_ksio4(cls, v):
    if not (isinstance(v, float) and v > 0):
        raise ValueError("ksio4 must be a positive float")
    return v

check_kns classmethod

check_kns(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("kns")
@classmethod
def check_kns(cls, v):
    if not (
        isinstance(v, list)
        and len(v) == 2
        and all(isinstance(x, float) and x >= 0 for x in v)
    ):
        raise ValueError("kns must be a list of 2 non-negative floats")
    return v

check_alphas classmethod

check_alphas(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("alphas")
@classmethod
def check_alphas(cls, v):
    if not (
        isinstance(v, list)
        and len(v) == 2
        and all(isinstance(x, float) and x > 0 for x in v)
    ):
        raise ValueError("alphas must be a list of 2 positive floats")
    return v

check_betas classmethod

check_betas(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("betas")
@classmethod
def check_betas(cls, v):
    if not (
        isinstance(v, list)
        and len(v) == 2
        and all(isinstance(x, float) and x >= 0 for x in v)
    ):
        raise ValueError("betas must be a list of 2 non-negative floats")
    return v

check_aks classmethod

check_aks(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("aks")
@classmethod
def check_aks(cls, v):
    if not (
        isinstance(v, list)
        and len(v) == 3
        and all(isinstance(x, float) and x > 0 for x in v)
    ):
        raise ValueError("aks must be a list of 3 positive floats")
    return v

check_betaz classmethod

check_betaz(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("betaz")
@classmethod
def check_betaz(cls, v):
    if not (
        isinstance(v, list)
        and len(v) == 2
        and all(isinstance(x, float) and x > 0 for x in v)
    ):
        raise ValueError("betaz must be a list of 2 positive floats")
    return v

check_alphaz classmethod

check_alphaz(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("alphaz")
@classmethod
def check_alphaz(cls, v):
    if not (
        isinstance(v, list)
        and len(v) == 2
        and all(isinstance(x, float) and 0 < x <= 1 for x in v)
    ):
        raise ValueError("alphaz must be a list of 2 floats between 0 and 1")
    return v

check_gammaz classmethod

check_gammaz(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("gammaz")
@classmethod
def check_gammaz(cls, v):
    if not (
        isinstance(v, list)
        and len(v) == 2
        and all(isinstance(x, float) and 0 < x <= 1 for x in v)
    ):
        raise ValueError("gammaz must be a list of 2 floats between 0 and 1")
    return v

check_kez classmethod

check_kez(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("kez")
@classmethod
def check_kez(cls, v):
    if not (
        isinstance(v, list)
        and len(v) == 2
        and all(isinstance(x, float) and 0 < x <= 1 for x in v)
    ):
        raise ValueError("kez must be a list of 2 floats between 0 and 1")
    return v

check_kgz classmethod

check_kgz(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("kgz")
@classmethod
def check_kgz(cls, v):
    if not (
        isinstance(v, list)
        and len(v) == 2
        and all(isinstance(x, float) and x > 0 for x in v)
    ):
        raise ValueError("kgz must be a list of 2 positive floats")
    return v

check_rhoz classmethod

check_rhoz(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("rhoz")
@classmethod
def check_rhoz(cls, v):
    if not (
        isinstance(v, list)
        and len(v) == 3
        and all(isinstance(x, float) and 0 <= x <= 1 for x in v)
    ):
        raise ValueError("rhoz must be a list of 3 floats between 0 and 1")
    return v

check_ipo4 classmethod

check_ipo4(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("ipo4")
@classmethod
def check_ipo4(cls, v):
    if v not in [0, 1]:
        raise ValueError("ipo4 must be either 0 or 1")
    return v

check_tr classmethod

check_tr(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("tr")
@classmethod
def check_tr(cls, v):
    if not (isinstance(v, float) and v > 0):
        raise ValueError("tr must be a positive float")
    return v

check_kox classmethod

check_kox(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("kox")
@classmethod
def check_kox(cls, v):
    if not (isinstance(v, float) and v > 0):
        raise ValueError("kox must be a positive float")
    return v

check_wss2 classmethod

check_wss2(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("wss2")
@classmethod
def check_wss2(cls, v):
    if not (isinstance(v, float) and v >= 0):
        raise ValueError("wss2 must be a non-negative float")
    return v

check_wsdn classmethod

check_wsdn(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("wsdn")
@classmethod
def check_wsdn(cls, v):
    if not (isinstance(v, float) and v >= 0):
        raise ValueError("wsdn must be a non-negative float")
    return v

check_wsdsi classmethod

check_wsdsi(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("wsdsi")
@classmethod
def check_wsdsi(cls, v):
    if not (isinstance(v, float) and v >= 0):
        raise ValueError("wsdsi must be a non-negative float")
    return v

check_si2n classmethod

check_si2n(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("si2n")
@classmethod
def check_si2n(cls, v):
    if not (isinstance(v, float) and v > 0):
        raise ValueError("si2n must be a positive float")
    return v

check_p2n classmethod

check_p2n(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("p2n")
@classmethod
def check_p2n(cls, v):
    if not (isinstance(v, float) and v > 0):
        raise ValueError("p2n must be a positive float")
    return v

check_o2no classmethod

check_o2no(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("o2no")
@classmethod
def check_o2no(cls, v):
    if not (isinstance(v, float) and v > 0):
        raise ValueError("o2no must be a positive float")
    return v

check_o2nh classmethod

check_o2nh(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("o2nh")
@classmethod
def check_o2nh(cls, v):
    if not (isinstance(v, float) and v > 0):
        raise ValueError("o2nh must be a positive float")
    return v

check_c2n classmethod

check_c2n(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("c2n")
@classmethod
def check_c2n(cls, v):
    if not (isinstance(v, float) and v > 0):
        raise ValueError("c2n must be a positive float")
    return v

check_gamman classmethod

check_gamman(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("gamman")
@classmethod
def check_gamman(cls, v):
    if not (isinstance(v, float) and 0 <= v <= 1):
        raise ValueError("gamman must be a float between 0 and 1")
    return v

check_pco2a classmethod

check_pco2a(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("pco2a")
@classmethod
def check_pco2a(cls, v):
    if not (isinstance(v, float) and v > 0):
        raise ValueError("pco2a must be a positive float")
    return v

check_kmdn classmethod

check_kmdn(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("kmdn")
@classmethod
def check_kmdn(cls, v):
    if not (
        isinstance(v, list)
        and len(v) == 2
        and all(isinstance(x, float) and x >= 0 for x in v)
    ):
        raise ValueError("kmdn must be a list of 2 non-negative floats")
    return v

check_kmdsi classmethod

check_kmdsi(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("kmdsi")
@classmethod
def check_kmdsi(cls, v):
    if not (
        isinstance(v, list)
        and len(v) == 2
        and all(isinstance(x, float) and x >= 0 for x in v)
    ):
        raise ValueError("kmdsi must be a list of 2 non-negative floats")
    return v

check_rhoz_sum

check_rhoz_sum()
Source code in rompy_schism/namelists/cosine.py
@model_validator(mode="after")
def check_rhoz_sum(self):
    if sum(self.rhoz) != 1:
        raise ValueError("Sum of rhoz values must equal 1")
    return self

Marco

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/cosine.py
class Marco(NamelistBaseModel):
    idelay: Optional[int] = Field(
        0, description="Switch for 7-day delay in zooplankton predation (0: off, 1: on)"
    )
    ndelay: Optional[int] = Field(
        7,
        description="Number of days for zooplankton predation delay when idelay is active",
    )
    ibgraze: Optional[int] = Field(
        0, description="Switch for bottom grazing function (0: off, 1: on)"
    )
    idapt: Optional[int] = Field(
        0, description="Switch for light adaptation (0: off, 1: on)"
    )
    alpha_corr: Optional[float] = Field(
        1.25, description="Correction factor for light adaptation when idapt is active"
    )
    zeptic: Optional[float] = Field(
        10.0,
        description="Euphotic zone depth parameter for light adaptation when idapt is active",
    )
    iz2graze: Optional[int] = Field(
        1, description="Switch for Z2 grazing on S2, Z1, and DN (0: off, 1: on)"
    )
    iout_cosine: Optional[int] = Field(
        0,
        description="CoSiNE model station output option (0: off, 1-5: various output levels)",
    )
    nspool_cosine: Optional[int] = Field(
        30,
        description="Output interval (number of time steps) for CoSiNE model station output",
    )
    ico2s: Optional[int] = Field(
        0,
        description="Switch for CO2 limitation on phytoplankton growth (0: off, 1: on)",
    )
    ispm: Optional[int] = Field(
        0,
        description="Suspended Particulate Matter (SPM) calculation method (0: constant, 1: spatial varying, 2: SED model)",
    )
    spm0: Optional[float] = Field(
        20.0, description="Constant SPM value used when ispm is 0"
    )
    ised: Optional[int] = Field(
        1, description="Switch for sediment flux model (0: off, 1: on)"
    )

    @field_validator("idelay")
    @classmethod
    def validate_idelay(cls, v):
        if v not in [0, 1]:
            raise ValueError("idelay must be 0 or 1")
        return v

    @field_validator("ndelay")
    @classmethod
    def validate_ndelay(cls, v):
        if v < 0:
            raise ValueError("ndelay must be non-negative")
        return v

    @field_validator("ibgraze")
    @classmethod
    def validate_ibgraze(cls, v):
        if v not in [0, 1]:
            raise ValueError("ibgraze must be 0 or 1")
        return v

    @field_validator("idapt")
    @classmethod
    def validate_idapt(cls, v):
        if v not in [0, 1]:
            raise ValueError("idapt must be 0 or 1")
        return v

    @field_validator("alpha_corr")
    @classmethod
    def validate_alpha_corr(cls, v):
        if v <= 0:
            raise ValueError("alpha_corr must be positive")
        return v

    @field_validator("zeptic")
    @classmethod
    def validate_zeptic(cls, v):
        if v <= 0:
            raise ValueError("zeptic must be positive")
        return v

    @field_validator("iz2graze")
    @classmethod
    def validate_iz2graze(cls, v):
        if v not in [0, 1]:
            raise ValueError("iz2graze must be 0 or 1")
        return v

    @field_validator("iout_cosine")
    @classmethod
    def validate_iout_cosine(cls, v):
        if v not in range(6):
            raise ValueError("iout_cosine must be between 0 and 5")
        return v

    @field_validator("nspool_cosine")
    @classmethod
    def validate_nspool_cosine(cls, v):
        if v <= 0:
            raise ValueError("nspool_cosine must be positive")
        return v

    @field_validator("ico2s")
    @classmethod
    def validate_ico2s(cls, v):
        if v not in [0, 1]:
            raise ValueError("ico2s must be 0 or 1")
        return v

    @field_validator("ispm")
    @classmethod
    def validate_ispm(cls, v):
        if v not in [0, 1, 2]:
            raise ValueError("ispm must be 0, 1, or 2")
        return v

    @field_validator("spm0")
    @classmethod
    def validate_spm0(cls, v):
        if v < 0:
            raise ValueError("spm0 must be non-negative")
        return v

    @field_validator("ised")
    @classmethod
    def validate_ised(cls, v):
        if v not in [0, 1]:
            raise ValueError("ised must be 0 or 1")
        return v

Attributes

idelay class-attribute instance-attribute

idelay: Optional[int] = Field(0, description='Switch for 7-day delay in zooplankton predation (0: off, 1: on)')

ndelay class-attribute instance-attribute

ndelay: Optional[int] = Field(7, description='Number of days for zooplankton predation delay when idelay is active')

ibgraze class-attribute instance-attribute

ibgraze: Optional[int] = Field(0, description='Switch for bottom grazing function (0: off, 1: on)')

idapt class-attribute instance-attribute

idapt: Optional[int] = Field(0, description='Switch for light adaptation (0: off, 1: on)')

alpha_corr class-attribute instance-attribute

alpha_corr: Optional[float] = Field(1.25, description='Correction factor for light adaptation when idapt is active')

zeptic class-attribute instance-attribute

zeptic: Optional[float] = Field(10.0, description='Euphotic zone depth parameter for light adaptation when idapt is active')

iz2graze class-attribute instance-attribute

iz2graze: Optional[int] = Field(1, description='Switch for Z2 grazing on S2, Z1, and DN (0: off, 1: on)')

iout_cosine class-attribute instance-attribute

iout_cosine: Optional[int] = Field(0, description='CoSiNE model station output option (0: off, 1-5: various output levels)')

nspool_cosine class-attribute instance-attribute

nspool_cosine: Optional[int] = Field(30, description='Output interval (number of time steps) for CoSiNE model station output')

ico2s class-attribute instance-attribute

ico2s: Optional[int] = Field(0, description='Switch for CO2 limitation on phytoplankton growth (0: off, 1: on)')

ispm class-attribute instance-attribute

ispm: Optional[int] = Field(0, description='Suspended Particulate Matter (SPM) calculation method (0: constant, 1: spatial varying, 2: SED model)')

spm0 class-attribute instance-attribute

spm0: Optional[float] = Field(20.0, description='Constant SPM value used when ispm is 0')

ised class-attribute instance-attribute

ised: Optional[int] = Field(1, description='Switch for sediment flux model (0: off, 1: on)')

Functions

validate_idelay classmethod

validate_idelay(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("idelay")
@classmethod
def validate_idelay(cls, v):
    if v not in [0, 1]:
        raise ValueError("idelay must be 0 or 1")
    return v

validate_ndelay classmethod

validate_ndelay(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("ndelay")
@classmethod
def validate_ndelay(cls, v):
    if v < 0:
        raise ValueError("ndelay must be non-negative")
    return v

validate_ibgraze classmethod

validate_ibgraze(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("ibgraze")
@classmethod
def validate_ibgraze(cls, v):
    if v not in [0, 1]:
        raise ValueError("ibgraze must be 0 or 1")
    return v

validate_idapt classmethod

validate_idapt(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("idapt")
@classmethod
def validate_idapt(cls, v):
    if v not in [0, 1]:
        raise ValueError("idapt must be 0 or 1")
    return v

validate_alpha_corr classmethod

validate_alpha_corr(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("alpha_corr")
@classmethod
def validate_alpha_corr(cls, v):
    if v <= 0:
        raise ValueError("alpha_corr must be positive")
    return v

validate_zeptic classmethod

validate_zeptic(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("zeptic")
@classmethod
def validate_zeptic(cls, v):
    if v <= 0:
        raise ValueError("zeptic must be positive")
    return v

validate_iz2graze classmethod

validate_iz2graze(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("iz2graze")
@classmethod
def validate_iz2graze(cls, v):
    if v not in [0, 1]:
        raise ValueError("iz2graze must be 0 or 1")
    return v

validate_iout_cosine classmethod

validate_iout_cosine(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("iout_cosine")
@classmethod
def validate_iout_cosine(cls, v):
    if v not in range(6):
        raise ValueError("iout_cosine must be between 0 and 5")
    return v

validate_nspool_cosine classmethod

validate_nspool_cosine(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("nspool_cosine")
@classmethod
def validate_nspool_cosine(cls, v):
    if v <= 0:
        raise ValueError("nspool_cosine must be positive")
    return v

validate_ico2s classmethod

validate_ico2s(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("ico2s")
@classmethod
def validate_ico2s(cls, v):
    if v not in [0, 1]:
        raise ValueError("ico2s must be 0 or 1")
    return v

validate_ispm classmethod

validate_ispm(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("ispm")
@classmethod
def validate_ispm(cls, v):
    if v not in [0, 1, 2]:
        raise ValueError("ispm must be 0, 1, or 2")
    return v

validate_spm0 classmethod

validate_spm0(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("spm0")
@classmethod
def validate_spm0(cls, v):
    if v < 0:
        raise ValueError("spm0 must be non-negative")
    return v

validate_ised classmethod

validate_ised(v)
Source code in rompy_schism/namelists/cosine.py
@field_validator("ised")
@classmethod
def validate_ised(cls, v):
    if v not in [0, 1]:
        raise ValueError("ised must be 0 or 1")
    return v

Misc

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/cosine.py
class Misc(NamelistBaseModel):
    iws: Optional[int] = Field(
        0,
        description="Flag to enable or disable diatom sinking velocity dependence on NO3 concentration",
    )
    no3c: Optional[float] = Field(
        2.0,
        description="Critical NO3 concentration (mmol/m3) for diatom sinking velocity calculation",
    )
    ws1: Optional[float] = Field(
        2.5,
        description="Diatom sinking velocity (m/day) when NO3 concentration is below no3c",
    )
    ws2: Optional[float] = Field(
        2.0,
        description="Diatom sinking velocity (m/day) when NO3 concentration is above no3c",
    )
    iclam: Optional[int] = Field(
        0, description="Flag to enable or disable clam grazing model"
    )
    deltaz: Optional[int] = Field(
        1, description="Vertical grid size (meter) for clam grazing model"
    )
    kcex: Optional[float] = Field(0.002, description="Clam excretion rate (day^-1)")
    nperclam: Optional[float] = Field(
        0.39032, description="Nitrogen content per clam (mmol[N])"
    )
    wclam: Optional[float] = Field(0.00545, description="Clam weight (g)")
    fclam: Optional[int] = Field(
        40, description="Clam filtration rate (L.g[AFDW]^-1.day^-1)"
    )
    nclam0: Optional[int] = Field(2000, description="Initial number of clams")
    fs2: Optional[list] = Field(
        [0.1, 0.1, 0.8],
        description="Partitioning coefficients for S2 from water column into sediment (3 values)",
    )
    rks2: Optional[list] = Field(
        [0.004, 0.0001, 0.0],
        description="Changing rates of remineralization for sediment S2 (3 values, day^-1)",
    )
    mks2: Optional[list] = Field(
        [0.1, 0.01, 0.0],
        description="Maximum remineralization rates for sediment S2 (3 values, day^-1)",
    )
    fdn: Optional[list] = Field(
        [0.15, 0.1, 0.75],
        description="Partitioning coefficients for DN from water column into sediment (3 values)",
    )
    rkdn: Optional[list] = Field(
        [0.004, 0.0001, 0.0],
        description="Changing rates of remineralization for sediment DN (3 values, day^-1)",
    )
    mkdn: Optional[list] = Field(
        [0.1, 0.01, 0.0],
        description="Maximum remineralization rates for sediment DN (3 values, day^-1)",
    )
    fdsi: Optional[list] = Field(
        [0.3, 0.3, 0.4],
        description="Partitioning coefficients for DSi from water column into sediment (3 values)",
    )
    rkdsi: Optional[list] = Field(
        [0.004, 0.0001, 0.0],
        description="Changing rates of remineralization for sediment DSi (3 values, day^-1)",
    )
    mkdsi: Optional[list] = Field(
        [0.1, 0.01, 0.0],
        description="Maximum remineralization rates for sediment DSi (3 values, day^-1)",
    )

    @field_validator("iws")
    @classmethod
    def validate_iws(cls, v: int) -> int:
        if v not in [0, 1]:
            raise ValueError("iws must be either 0 or 1")
        return v

    @field_validator("no3c")
    @classmethod
    def validate_no3c(cls, v: float) -> float:
        if v <= 0:
            raise ValueError("no3c must be positive")
        return v

    @field_validator("ws1")
    @classmethod
    def validate_ws1(cls, v: float) -> float:
        if v < 0:
            raise ValueError("ws1 must be non-negative")
        return v

    @field_validator("ws2")
    @classmethod
    def validate_ws2(cls, v: float) -> float:
        if v < 0:
            raise ValueError("ws2 must be non-negative")
        return v

    @field_validator("iclam")
    @classmethod
    def validate_iclam(cls, v: int) -> int:
        if v not in [0, 1]:
            raise ValueError("iclam must be either 0 or 1")
        return v

    @field_validator("deltaz")
    @classmethod
    def validate_deltaz(cls, v: float) -> float:
        if v <= 0:
            raise ValueError("deltaz must be positive")
        return v

    @field_validator("kcex")
    @classmethod
    def validate_kcex(cls, v: float) -> float:
        if v < 0 or v > 1:
            raise ValueError("kcex must be between 0 and 1")
        return v

    @field_validator("nperclam")
    @classmethod
    def validate_nperclam(cls, v: float) -> float:
        if v <= 0:
            raise ValueError("nperclam must be positive")
        return v

    @field_validator("wclam")
    @classmethod
    def validate_wclam(cls, v: float) -> float:
        if v <= 0:
            raise ValueError("wclam must be positive")
        return v

    @field_validator("fclam")
    @classmethod
    def validate_fclam(cls, v: float) -> float:
        if v <= 0:
            raise ValueError("fclam must be positive")
        return v

    @field_validator("nclam0")
    @classmethod
    def validate_nclam0(cls, v: int) -> int:
        if v < 0:
            raise ValueError("nclam0 must be non-negative")
        return v

    @field_validator("fs2")
    @classmethod
    def validate_fs2(cls, v: List[float]) -> List[float]:
        if len(v) != 3 or not all(0 <= x <= 1 for x in v) or abs(sum(v) - 1) > 1e-6:
            raise ValueError(
                "fs2 must be a list of 3 values between 0 and 1, summing to 1"
            )
        return v

    @field_validator("rks2")
    @classmethod
    def validate_rks2(cls, v: List[float]) -> List[float]:
        if len(v) != 3 or not all(x >= 0 for x in v):
            raise ValueError("rks2 must be a list of 3 non-negative values")
        return v

    @field_validator("mks2")
    @classmethod
    def validate_mks2(cls, v: List[float]) -> List[float]:
        if len(v) != 3 or not all(x >= 0 for x in v):
            raise ValueError("mks2 must be a list of 3 non-negative values")
        return v

    @field_validator("fdn")
    @classmethod
    def validate_fdn(cls, v: List[float]) -> List[float]:
        if len(v) != 3 or not all(0 <= x <= 1 for x in v) or abs(sum(v) - 1) > 1e-6:
            raise ValueError(
                "fdn must be a list of 3 values between 0 and 1, summing to 1"
            )
        return v

    @field_validator("rkdn")
    @classmethod
    def validate_rkdn(cls, v: List[float]) -> List[float]:
        if len(v) != 3 or not all(x >= 0 for x in v):
            raise ValueError("rkdn must be a list of 3 non-negative values")
        return v

    @field_validator("mkdn")
    @classmethod
    def validate_mkdn(cls, v: List[float]) -> List[float]:
        if len(v) != 3 or not all(x >= 0 for x in v):
            raise ValueError("mkdn must be a list of 3 non-negative values")
        return v

    @field_validator("fdsi")
    @classmethod
    def validate_fdsi(cls, v: List[float]) -> List[float]:
        if len(v) != 3 or not all(0 <= x <= 1 for x in v) or abs(sum(v) - 1) > 1e-6:
            raise ValueError(
                "fdsi must be a list of 3 values between 0 and 1, summing to 1"
            )
        return v

    @field_validator("rkdsi")
    @classmethod
    def validate_rkdsi(cls, v: List[float]) -> List[float]:
        if len(v) != 3 or not all(x >= 0 for x in v):
            raise ValueError("rkdsi must be a list of 3 non-negative values")
        return v

    @field_validator("mkdsi")
    @classmethod
    def validate_mkdsi(cls, v: List[float]) -> List[float]:
        if len(v) != 3 or not all(x >= 0 for x in v):
            raise ValueError("mkdsi must be a list of 3 non-negative values")
        return v

Attributes

iws class-attribute instance-attribute

iws: Optional[int] = Field(0, description='Flag to enable or disable diatom sinking velocity dependence on NO3 concentration')

no3c class-attribute instance-attribute

no3c: Optional[float] = Field(2.0, description='Critical NO3 concentration (mmol/m3) for diatom sinking velocity calculation')

ws1 class-attribute instance-attribute

ws1: Optional[float] = Field(2.5, description='Diatom sinking velocity (m/day) when NO3 concentration is below no3c')

ws2 class-attribute instance-attribute

ws2: Optional[float] = Field(2.0, description='Diatom sinking velocity (m/day) when NO3 concentration is above no3c')

iclam class-attribute instance-attribute

iclam: Optional[int] = Field(0, description='Flag to enable or disable clam grazing model')

deltaz class-attribute instance-attribute

deltaz: Optional[int] = Field(1, description='Vertical grid size (meter) for clam grazing model')

kcex class-attribute instance-attribute

kcex: Optional[float] = Field(0.002, description='Clam excretion rate (day^-1)')

nperclam class-attribute instance-attribute

nperclam: Optional[float] = Field(0.39032, description='Nitrogen content per clam (mmol[N])')

wclam class-attribute instance-attribute

wclam: Optional[float] = Field(0.00545, description='Clam weight (g)')

fclam class-attribute instance-attribute

fclam: Optional[int] = Field(40, description='Clam filtration rate (L.g[AFDW]^-1.day^-1)')

nclam0 class-attribute instance-attribute

nclam0: Optional[int] = Field(2000, description='Initial number of clams')

fs2 class-attribute instance-attribute

fs2: Optional[list] = Field([0.1, 0.1, 0.8], description='Partitioning coefficients for S2 from water column into sediment (3 values)')

rks2 class-attribute instance-attribute

rks2: Optional[list] = Field([0.004, 0.0001, 0.0], description='Changing rates of remineralization for sediment S2 (3 values, day^-1)')

mks2 class-attribute instance-attribute

mks2: Optional[list] = Field([0.1, 0.01, 0.0], description='Maximum remineralization rates for sediment S2 (3 values, day^-1)')

fdn class-attribute instance-attribute

fdn: Optional[list] = Field([0.15, 0.1, 0.75], description='Partitioning coefficients for DN from water column into sediment (3 values)')

rkdn class-attribute instance-attribute

rkdn: Optional[list] = Field([0.004, 0.0001, 0.0], description='Changing rates of remineralization for sediment DN (3 values, day^-1)')

mkdn class-attribute instance-attribute

mkdn: Optional[list] = Field([0.1, 0.01, 0.0], description='Maximum remineralization rates for sediment DN (3 values, day^-1)')

fdsi class-attribute instance-attribute

fdsi: Optional[list] = Field([0.3, 0.3, 0.4], description='Partitioning coefficients for DSi from water column into sediment (3 values)')

rkdsi class-attribute instance-attribute

rkdsi: Optional[list] = Field([0.004, 0.0001, 0.0], description='Changing rates of remineralization for sediment DSi (3 values, day^-1)')

mkdsi class-attribute instance-attribute

mkdsi: Optional[list] = Field([0.1, 0.01, 0.0], description='Maximum remineralization rates for sediment DSi (3 values, day^-1)')

Functions

validate_iws classmethod

validate_iws(v: int) -> int
Source code in rompy_schism/namelists/cosine.py
@field_validator("iws")
@classmethod
def validate_iws(cls, v: int) -> int:
    if v not in [0, 1]:
        raise ValueError("iws must be either 0 or 1")
    return v

validate_no3c classmethod

validate_no3c(v: float) -> float
Source code in rompy_schism/namelists/cosine.py
@field_validator("no3c")
@classmethod
def validate_no3c(cls, v: float) -> float:
    if v <= 0:
        raise ValueError("no3c must be positive")
    return v

validate_ws1 classmethod

validate_ws1(v: float) -> float
Source code in rompy_schism/namelists/cosine.py
@field_validator("ws1")
@classmethod
def validate_ws1(cls, v: float) -> float:
    if v < 0:
        raise ValueError("ws1 must be non-negative")
    return v

validate_ws2 classmethod

validate_ws2(v: float) -> float
Source code in rompy_schism/namelists/cosine.py
@field_validator("ws2")
@classmethod
def validate_ws2(cls, v: float) -> float:
    if v < 0:
        raise ValueError("ws2 must be non-negative")
    return v

validate_iclam classmethod

validate_iclam(v: int) -> int
Source code in rompy_schism/namelists/cosine.py
@field_validator("iclam")
@classmethod
def validate_iclam(cls, v: int) -> int:
    if v not in [0, 1]:
        raise ValueError("iclam must be either 0 or 1")
    return v

validate_deltaz classmethod

validate_deltaz(v: float) -> float
Source code in rompy_schism/namelists/cosine.py
@field_validator("deltaz")
@classmethod
def validate_deltaz(cls, v: float) -> float:
    if v <= 0:
        raise ValueError("deltaz must be positive")
    return v

validate_kcex classmethod

validate_kcex(v: float) -> float
Source code in rompy_schism/namelists/cosine.py
@field_validator("kcex")
@classmethod
def validate_kcex(cls, v: float) -> float:
    if v < 0 or v > 1:
        raise ValueError("kcex must be between 0 and 1")
    return v

validate_nperclam classmethod

validate_nperclam(v: float) -> float
Source code in rompy_schism/namelists/cosine.py
@field_validator("nperclam")
@classmethod
def validate_nperclam(cls, v: float) -> float:
    if v <= 0:
        raise ValueError("nperclam must be positive")
    return v

validate_wclam classmethod

validate_wclam(v: float) -> float
Source code in rompy_schism/namelists/cosine.py
@field_validator("wclam")
@classmethod
def validate_wclam(cls, v: float) -> float:
    if v <= 0:
        raise ValueError("wclam must be positive")
    return v

validate_fclam classmethod

validate_fclam(v: float) -> float
Source code in rompy_schism/namelists/cosine.py
@field_validator("fclam")
@classmethod
def validate_fclam(cls, v: float) -> float:
    if v <= 0:
        raise ValueError("fclam must be positive")
    return v

validate_nclam0 classmethod

validate_nclam0(v: int) -> int
Source code in rompy_schism/namelists/cosine.py
@field_validator("nclam0")
@classmethod
def validate_nclam0(cls, v: int) -> int:
    if v < 0:
        raise ValueError("nclam0 must be non-negative")
    return v

validate_fs2 classmethod

validate_fs2(v: List[float]) -> List[float]
Source code in rompy_schism/namelists/cosine.py
@field_validator("fs2")
@classmethod
def validate_fs2(cls, v: List[float]) -> List[float]:
    if len(v) != 3 or not all(0 <= x <= 1 for x in v) or abs(sum(v) - 1) > 1e-6:
        raise ValueError(
            "fs2 must be a list of 3 values between 0 and 1, summing to 1"
        )
    return v

validate_rks2 classmethod

validate_rks2(v: List[float]) -> List[float]
Source code in rompy_schism/namelists/cosine.py
@field_validator("rks2")
@classmethod
def validate_rks2(cls, v: List[float]) -> List[float]:
    if len(v) != 3 or not all(x >= 0 for x in v):
        raise ValueError("rks2 must be a list of 3 non-negative values")
    return v

validate_mks2 classmethod

validate_mks2(v: List[float]) -> List[float]
Source code in rompy_schism/namelists/cosine.py
@field_validator("mks2")
@classmethod
def validate_mks2(cls, v: List[float]) -> List[float]:
    if len(v) != 3 or not all(x >= 0 for x in v):
        raise ValueError("mks2 must be a list of 3 non-negative values")
    return v

validate_fdn classmethod

validate_fdn(v: List[float]) -> List[float]
Source code in rompy_schism/namelists/cosine.py
@field_validator("fdn")
@classmethod
def validate_fdn(cls, v: List[float]) -> List[float]:
    if len(v) != 3 or not all(0 <= x <= 1 for x in v) or abs(sum(v) - 1) > 1e-6:
        raise ValueError(
            "fdn must be a list of 3 values between 0 and 1, summing to 1"
        )
    return v

validate_rkdn classmethod

validate_rkdn(v: List[float]) -> List[float]
Source code in rompy_schism/namelists/cosine.py
@field_validator("rkdn")
@classmethod
def validate_rkdn(cls, v: List[float]) -> List[float]:
    if len(v) != 3 or not all(x >= 0 for x in v):
        raise ValueError("rkdn must be a list of 3 non-negative values")
    return v

validate_mkdn classmethod

validate_mkdn(v: List[float]) -> List[float]
Source code in rompy_schism/namelists/cosine.py
@field_validator("mkdn")
@classmethod
def validate_mkdn(cls, v: List[float]) -> List[float]:
    if len(v) != 3 or not all(x >= 0 for x in v):
        raise ValueError("mkdn must be a list of 3 non-negative values")
    return v

validate_fdsi classmethod

validate_fdsi(v: List[float]) -> List[float]
Source code in rompy_schism/namelists/cosine.py
@field_validator("fdsi")
@classmethod
def validate_fdsi(cls, v: List[float]) -> List[float]:
    if len(v) != 3 or not all(0 <= x <= 1 for x in v) or abs(sum(v) - 1) > 1e-6:
        raise ValueError(
            "fdsi must be a list of 3 values between 0 and 1, summing to 1"
        )
    return v

validate_rkdsi classmethod

validate_rkdsi(v: List[float]) -> List[float]
Source code in rompy_schism/namelists/cosine.py
@field_validator("rkdsi")
@classmethod
def validate_rkdsi(cls, v: List[float]) -> List[float]:
    if len(v) != 3 or not all(x >= 0 for x in v):
        raise ValueError("rkdsi must be a list of 3 non-negative values")
    return v

validate_mkdsi classmethod

validate_mkdsi(v: List[float]) -> List[float]
Source code in rompy_schism/namelists/cosine.py
@field_validator("mkdsi")
@classmethod
def validate_mkdsi(cls, v: List[float]) -> List[float]:
    if len(v) != 3 or not all(x >= 0 for x in v):
        raise ValueError("mkdsi must be a list of 3 non-negative values")
    return v

Cosine

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/cosine.py
class Cosine(NamelistBaseModel):
    marco: Optional[Marco] = Field(default_factory=Marco)
    core: Optional[Core] = Field(default_factory=Core)
    misc: Optional[Misc] = Field(default_factory=Misc)

Attributes

marco class-attribute instance-attribute

marco: Optional[Marco] = Field(default_factory=Marco)

core class-attribute instance-attribute

core: Optional[Core] = Field(default_factory=Core)

misc class-attribute instance-attribute

misc: Optional[Misc] = Field(default_factory=Misc)

WWMINPUT

Coupl

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/wwminput.py
class Coupl(NamelistBaseModel):
    lcpl: Optional[bool] = Field(
        True,
        description="Main switch to enable coupling with the current model. Should be kept on for SCHISM-WWM coupling.",
    )
    radflag: Optional[str] = Field(
        "LON",
        description="Determines the formulation for wave-induced forces. 'LON' for Longuet-Higgins formulation, 'VOR' for vortex formulation. Usually set to 'LON'.",
    )
    letot: Optional[bool] = Field(
        False,
        description="Option to compute wave-induced radiation stress. If True, radiation stress is based on the integrated wave spectrum. If False (recommended), it's estimated using the directional spectra as described in Roland et al. (2008). False is preferred as it preserves spectral information.",
    )
    nlvt: Optional[int] = Field(
        10, description="Number of vertical layers. Not used when coupled with SCHISM."
    )
    dtcoup: Optional[float] = Field(
        600.0,
        description="Coupling time step in seconds. Not used when coupled to SCHISM.",
    )

    @field_validator("lcpl")
    @classmethod
    def validate_lcpl(cls, v):
        return v

    @field_validator("radflag")
    @classmethod
    def validate_radflag(cls, v):
        if v not in ["LON", "VOR"]:
            raise ValueError("radflag must be either 'LON' or 'VOR'")
        return v

    @field_validator("letot")
    @classmethod
    def validate_letot(cls, v):
        return v

    @field_validator("nlvt")
    @classmethod
    def validate_nlvt(cls, v):
        if v <= 0:
            raise ValueError("nlvt must be a positive integer")
        return v

    @field_validator("dtcoup")
    @classmethod
    def validate_dtcoup(cls, v):
        if v <= 0:
            raise ValueError("dtcoup must be a positive number")
        return v

Attributes

lcpl class-attribute instance-attribute

lcpl: Optional[bool] = Field(True, description='Main switch to enable coupling with the current model. Should be kept on for SCHISM-WWM coupling.')

radflag class-attribute instance-attribute

radflag: Optional[str] = Field('LON', description="Determines the formulation for wave-induced forces. 'LON' for Longuet-Higgins formulation, 'VOR' for vortex formulation. Usually set to 'LON'.")

letot class-attribute instance-attribute

letot: Optional[bool] = Field(False, description="Option to compute wave-induced radiation stress. If True, radiation stress is based on the integrated wave spectrum. If False (recommended), it's estimated using the directional spectra as described in Roland et al. (2008). False is preferred as it preserves spectral information.")

nlvt class-attribute instance-attribute

nlvt: Optional[int] = Field(10, description='Number of vertical layers. Not used when coupled with SCHISM.')

dtcoup class-attribute instance-attribute

dtcoup: Optional[float] = Field(600.0, description='Coupling time step in seconds. Not used when coupled to SCHISM.')

Functions

validate_lcpl classmethod

validate_lcpl(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("lcpl")
@classmethod
def validate_lcpl(cls, v):
    return v

validate_radflag classmethod

validate_radflag(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("radflag")
@classmethod
def validate_radflag(cls, v):
    if v not in ["LON", "VOR"]:
        raise ValueError("radflag must be either 'LON' or 'VOR'")
    return v

validate_letot classmethod

validate_letot(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("letot")
@classmethod
def validate_letot(cls, v):
    return v

validate_nlvt classmethod

validate_nlvt(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("nlvt")
@classmethod
def validate_nlvt(cls, v):
    if v <= 0:
        raise ValueError("nlvt must be a positive integer")
    return v

validate_dtcoup classmethod

validate_dtcoup(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("dtcoup")
@classmethod
def validate_dtcoup(cls, v):
    if v <= 0:
        raise ValueError("dtcoup must be a positive number")
    return v

Engs

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/wwminput.py
class Engs(NamelistBaseModel):
    mesnl: Optional[int] = Field(
        1,
        description="Controls the nonlinear wave-wave interactions (NL4) using the Discrete Interaction Approximation. 1 enables the interactions, 0 disables them.",
    )
    mesin: Optional[int] = Field(
        1,
        description="Specifies the wind input formulation. Options include: 0 (no wind), 1 (Ardhuin et al.), 2 (ECMWF physics), 3 (Makin Stam), 4 (Babanin et al.), 5 (Cycle 3).",
    )
    ifric: Optional[int] = Field(
        1,
        description="Determines the formulation for the atmospheric boundary layer. Should be 1 when MESIN=1, and 4 when MESIN=3.",
    )
    mesbf: Optional[int] = Field(
        2,
        description="Selects the bottom friction formulation. 1 for JONSWAP (Default), 2 for Madsen et al. (1989), 3 for SHOWEX.",
    )
    fricc: Optional[float] = Field(
        0.006,
        description="Bottom friction coefficient or roughness, depending on MESBF. For MESBF=1: JONSWAP coefficient [0.038,0.067]. For MESBF=2: physical bottom roughness. For MESBF=3: D50 (negative value reads from SHOWEX_D50.gr3).",
    )
    mesbr: Optional[int] = Field(
        1, description="Enables (1) or disables (0) shallow water wave breaking."
    )
    ibreak: Optional[int] = Field(
        1,
        description="Selects the wave breaking formulation. Options range from 1 to 6, each representing a different model or approach.",
    )
    icrit: Optional[int] = Field(
        1,
        description="Specifies the wave breaking criterion. Options 1-6 represent different methods for determining the breaking point.",
    )
    brcr: Optional[float] = Field(
        0.73,
        description="Breaking criterion parameter. Its meaning depends on IBREAK and ICRIT settings.",
    )
    a_brcr: Optional[float] = Field(
        0.76,
        description="Coefficient used in ICRIT=4,5 for calculating the breaking criterion.",
    )
    b_brcr: Optional[float] = Field(
        0.29,
        description="Coefficient used in ICRIT=4,5 for calculating the breaking criterion.",
    )
    min_brcr: Optional[float] = Field(
        0.25, description="Minimum value for the breaking criterion when ICRIT=4,5."
    )
    max_brcr: Optional[float] = Field(
        0.8, description="Maximum value for the breaking criterion when ICRIT=4,5."
    )
    a_biph: Optional[float] = Field(
        0.2, description="Biphase coefficient, used when IBREAK=3."
    )
    br_coef_method: Optional[int] = Field(
        1,
        description="Method for determining the breaking coefficient. 1 for constant, 2 for adaptive.",
    )
    b_alp: Optional[float] = Field(
        0.5,
        description="Breaking coefficient. If BR_COEF_METHOD = 2, B_ALP should be around 40.",
    )
    zprof_break: Optional[int] = Field(
        2,
        description="Specifies the vertical distribution function of the wave breaking source term in 3D runs. Options 1-6 represent different distribution functions.",
    )
    bc_break: Optional[int] = Field(
        1,
        description="Controls the application of depth-limited breaking at boundaries. 1 to enable, 0 to disable.",
    )
    iroller: Optional[int] = Field(
        0,
        description="Enables (1) or disables (0) the wave roller model. Currently not in use.",
    )
    alprol: Optional[float] = Field(
        0.85,
        description="Alpha coefficient for the wave roller model, determining the energy transfer to the roller. Range: 0 to 1.",
    )
    meveg: Optional[int] = Field(
        0,
        description="Enables (1) or disables (0) vegetation effects. If enabled, isav must be 1 in param.nml.",
    )
    lmaxetot: Optional[bool] = Field(
        True,
        description="Controls the use of wave breaking limiter to limit shallow water wave height. True to enable, False to disable.",
    )
    mesds: Optional[int] = Field(
        1,
        description="Specifies the formulation for the whitecapping source function. Should have the same value as MESIN.",
    )
    mestr: Optional[int] = Field(
        1,
        description="Selects the formulation for triad 3 wave interactions. 0 (off), 1 (Lumped Triad Approx.), 2 (corrected LTA by Salmon et al. (2016)).",
    )
    trico: Optional[float] = Field(
        0.1,
        description="Proportionality constant (α_EB) for triad interactions. Default is 0.1.",
    )
    trira: Optional[float] = Field(
        2.5,
        description="Ratio of maximum frequency considered in triads over mean frequency. Suggested value is 2.5.",
    )
    triurs: Optional[float] = Field(
        0.1,
        description="Critical Ursell number for triad computations. Triads are not computed if Ursell number < TRIURS.",
    )

    @field_validator("mesnl")
    @classmethod
    def validate_mesnl(cls, v):
        if v not in [0, 1]:
            raise ValueError("MESNL must be either 0 or 1")
        return v

    @field_validator("mesin")
    @classmethod
    def validate_mesin(cls, v):
        if v not in range(6):
            raise ValueError("MESIN must be between 0 and 5")
        return v

    @field_validator("ifric")
    @classmethod
    def validate_ifric(cls, v, info):
        if info.data.get("mesin") == 1 and v != 1:
            raise ValueError("IFRIC should be 1 when MESIN is 1")
        elif info.data.get("mesin") == 3 and v != 4:
            raise ValueError("IFRIC should be 4 when MESIN is 3")
        return v

    @field_validator("mesbf")
    @classmethod
    def validate_mesbf(cls, v):
        if v not in [1, 2, 3]:
            raise ValueError("MESBF must be 1, 2, or 3")
        return v

    @field_validator("fricc")
    @classmethod
    def validate_fricc(cls, v, info):
        if info.data.get("mesbf") == 1 and not 0.038 <= v <= 0.067:
            raise ValueError("FRICC must be between 0.038 and 0.067 when MESBF is 1")
        return v

    @field_validator("mesbr")
    @classmethod
    def validate_mesbr(cls, v):
        if v not in [0, 1]:
            raise ValueError("MESBR must be either 0 or 1")
        return v

    @field_validator("ibreak")
    @classmethod
    def validate_ibreak(cls, v):
        if v not in range(1, 7):
            raise ValueError("IBREAK must be between 1 and 6")
        return v

    @field_validator("icrit")
    @classmethod
    def validate_icrit(cls, v):
        if v not in range(1, 7):
            raise ValueError("ICRIT must be between 1 and 6")
        return v

    @field_validator("brcr")
    @classmethod
    def validate_brcr(cls, v, info):
        if info.data.get("ibreak") in [1, 5] and v != 0.73:
            print("Warning: Default BRCR for IBREAK 1 or 5 is 0.73")
        elif info.data.get("ibreak") in [2, 3] and v != 0.42:
            print("Warning: Default BRCR for IBREAK 2 or 3 is 0.42")
        elif info.data.get("ibreak") == 4 and v != -1.3963:
            print("Warning: Default BRCR for IBREAK 4 is -1.3963")
        return v

    @field_validator("br_coef_method")
    @classmethod
    def validate_br_coef_method(cls, v):
        if v not in [1, 2]:
            raise ValueError("BR_COEF_METHOD must be either 1 or 2")
        return v

    @field_validator("b_alp")
    @classmethod
    def validate_b_alp(cls, v, info):
        if info.data.get("br_coef_method") == 2 and v != 40:
            print("Warning: B_ALP should be around 40 when BR_COEF_METHOD is 2")
        return v

    @field_validator("zprof_break")
    @classmethod
    def validate_zprof_break(cls, v):
        if v not in range(1, 7):
            raise ValueError("ZPROF_BREAK must be between 1 and 6")
        return v

    @field_validator("bc_break")
    @classmethod
    def validate_bc_break(cls, v):
        if v not in [0, 1]:
            raise ValueError("BC_BREAK must be either 0 or 1")
        return v

    @field_validator("iroller")
    @classmethod
    def validate_iroller(cls, v):
        if v not in [0, 1]:
            raise ValueError("IROLLER must be either 0 or 1")
        return v

    @field_validator("alprol")
    @classmethod
    def validate_alprol(cls, v):
        if not 0 <= v <= 1:
            raise ValueError("ALPROL must be between 0 and 1")
        return v

    @field_validator("meveg")
    @classmethod
    def validate_meveg(cls, v):
        if v not in [0, 1]:
            raise ValueError("MEVEG must be either 0 or 1")
        return v

    @field_validator("mesds")
    @classmethod
    def validate_mesds(cls, v, info):
        if v != info.data.get("mesin"):
            raise ValueError("MESDS should have the same value as MESIN")
        return v

    @field_validator("mestr")
    @classmethod
    def validate_mestr(cls, v):
        if v not in [0, 1, 2]:
            raise ValueError("MESTR must be 0, 1, or 2")
        return v

Attributes

mesnl class-attribute instance-attribute

mesnl: Optional[int] = Field(1, description='Controls the nonlinear wave-wave interactions (NL4) using the Discrete Interaction Approximation. 1 enables the interactions, 0 disables them.')

mesin class-attribute instance-attribute

mesin: Optional[int] = Field(1, description='Specifies the wind input formulation. Options include: 0 (no wind), 1 (Ardhuin et al.), 2 (ECMWF physics), 3 (Makin Stam), 4 (Babanin et al.), 5 (Cycle 3).')

ifric class-attribute instance-attribute

ifric: Optional[int] = Field(1, description='Determines the formulation for the atmospheric boundary layer. Should be 1 when MESIN=1, and 4 when MESIN=3.')

mesbf class-attribute instance-attribute

mesbf: Optional[int] = Field(2, description='Selects the bottom friction formulation. 1 for JONSWAP (Default), 2 for Madsen et al. (1989), 3 for SHOWEX.')

fricc class-attribute instance-attribute

fricc: Optional[float] = Field(0.006, description='Bottom friction coefficient or roughness, depending on MESBF. For MESBF=1: JONSWAP coefficient [0.038,0.067]. For MESBF=2: physical bottom roughness. For MESBF=3: D50 (negative value reads from SHOWEX_D50.gr3).')

mesbr class-attribute instance-attribute

mesbr: Optional[int] = Field(1, description='Enables (1) or disables (0) shallow water wave breaking.')

ibreak class-attribute instance-attribute

ibreak: Optional[int] = Field(1, description='Selects the wave breaking formulation. Options range from 1 to 6, each representing a different model or approach.')

icrit class-attribute instance-attribute

icrit: Optional[int] = Field(1, description='Specifies the wave breaking criterion. Options 1-6 represent different methods for determining the breaking point.')

brcr class-attribute instance-attribute

brcr: Optional[float] = Field(0.73, description='Breaking criterion parameter. Its meaning depends on IBREAK and ICRIT settings.')

a_brcr class-attribute instance-attribute

a_brcr: Optional[float] = Field(0.76, description='Coefficient used in ICRIT=4,5 for calculating the breaking criterion.')

b_brcr class-attribute instance-attribute

b_brcr: Optional[float] = Field(0.29, description='Coefficient used in ICRIT=4,5 for calculating the breaking criterion.')

min_brcr class-attribute instance-attribute

min_brcr: Optional[float] = Field(0.25, description='Minimum value for the breaking criterion when ICRIT=4,5.')

max_brcr class-attribute instance-attribute

max_brcr: Optional[float] = Field(0.8, description='Maximum value for the breaking criterion when ICRIT=4,5.')

a_biph class-attribute instance-attribute

a_biph: Optional[float] = Field(0.2, description='Biphase coefficient, used when IBREAK=3.')

br_coef_method class-attribute instance-attribute

br_coef_method: Optional[int] = Field(1, description='Method for determining the breaking coefficient. 1 for constant, 2 for adaptive.')

b_alp class-attribute instance-attribute

b_alp: Optional[float] = Field(0.5, description='Breaking coefficient. If BR_COEF_METHOD = 2, B_ALP should be around 40.')

zprof_break class-attribute instance-attribute

zprof_break: Optional[int] = Field(2, description='Specifies the vertical distribution function of the wave breaking source term in 3D runs. Options 1-6 represent different distribution functions.')

bc_break class-attribute instance-attribute

bc_break: Optional[int] = Field(1, description='Controls the application of depth-limited breaking at boundaries. 1 to enable, 0 to disable.')

iroller class-attribute instance-attribute

iroller: Optional[int] = Field(0, description='Enables (1) or disables (0) the wave roller model. Currently not in use.')

alprol class-attribute instance-attribute

alprol: Optional[float] = Field(0.85, description='Alpha coefficient for the wave roller model, determining the energy transfer to the roller. Range: 0 to 1.')

meveg class-attribute instance-attribute

meveg: Optional[int] = Field(0, description='Enables (1) or disables (0) vegetation effects. If enabled, isav must be 1 in param.nml.')

lmaxetot class-attribute instance-attribute

lmaxetot: Optional[bool] = Field(True, description='Controls the use of wave breaking limiter to limit shallow water wave height. True to enable, False to disable.')

mesds class-attribute instance-attribute

mesds: Optional[int] = Field(1, description='Specifies the formulation for the whitecapping source function. Should have the same value as MESIN.')

mestr class-attribute instance-attribute

mestr: Optional[int] = Field(1, description='Selects the formulation for triad 3 wave interactions. 0 (off), 1 (Lumped Triad Approx.), 2 (corrected LTA by Salmon et al. (2016)).')

trico class-attribute instance-attribute

trico: Optional[float] = Field(0.1, description='Proportionality constant (α_EB) for triad interactions. Default is 0.1.')

trira class-attribute instance-attribute

trira: Optional[float] = Field(2.5, description='Ratio of maximum frequency considered in triads over mean frequency. Suggested value is 2.5.')

triurs class-attribute instance-attribute

triurs: Optional[float] = Field(0.1, description='Critical Ursell number for triad computations. Triads are not computed if Ursell number < TRIURS.')

Functions

validate_mesnl classmethod

validate_mesnl(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("mesnl")
@classmethod
def validate_mesnl(cls, v):
    if v not in [0, 1]:
        raise ValueError("MESNL must be either 0 or 1")
    return v

validate_mesin classmethod

validate_mesin(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("mesin")
@classmethod
def validate_mesin(cls, v):
    if v not in range(6):
        raise ValueError("MESIN must be between 0 and 5")
    return v

validate_ifric classmethod

validate_ifric(v, info)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("ifric")
@classmethod
def validate_ifric(cls, v, info):
    if info.data.get("mesin") == 1 and v != 1:
        raise ValueError("IFRIC should be 1 when MESIN is 1")
    elif info.data.get("mesin") == 3 and v != 4:
        raise ValueError("IFRIC should be 4 when MESIN is 3")
    return v

validate_mesbf classmethod

validate_mesbf(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("mesbf")
@classmethod
def validate_mesbf(cls, v):
    if v not in [1, 2, 3]:
        raise ValueError("MESBF must be 1, 2, or 3")
    return v

validate_fricc classmethod

validate_fricc(v, info)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("fricc")
@classmethod
def validate_fricc(cls, v, info):
    if info.data.get("mesbf") == 1 and not 0.038 <= v <= 0.067:
        raise ValueError("FRICC must be between 0.038 and 0.067 when MESBF is 1")
    return v

validate_mesbr classmethod

validate_mesbr(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("mesbr")
@classmethod
def validate_mesbr(cls, v):
    if v not in [0, 1]:
        raise ValueError("MESBR must be either 0 or 1")
    return v

validate_ibreak classmethod

validate_ibreak(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("ibreak")
@classmethod
def validate_ibreak(cls, v):
    if v not in range(1, 7):
        raise ValueError("IBREAK must be between 1 and 6")
    return v

validate_icrit classmethod

validate_icrit(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("icrit")
@classmethod
def validate_icrit(cls, v):
    if v not in range(1, 7):
        raise ValueError("ICRIT must be between 1 and 6")
    return v

validate_brcr classmethod

validate_brcr(v, info)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("brcr")
@classmethod
def validate_brcr(cls, v, info):
    if info.data.get("ibreak") in [1, 5] and v != 0.73:
        print("Warning: Default BRCR for IBREAK 1 or 5 is 0.73")
    elif info.data.get("ibreak") in [2, 3] and v != 0.42:
        print("Warning: Default BRCR for IBREAK 2 or 3 is 0.42")
    elif info.data.get("ibreak") == 4 and v != -1.3963:
        print("Warning: Default BRCR for IBREAK 4 is -1.3963")
    return v

validate_br_coef_method classmethod

validate_br_coef_method(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("br_coef_method")
@classmethod
def validate_br_coef_method(cls, v):
    if v not in [1, 2]:
        raise ValueError("BR_COEF_METHOD must be either 1 or 2")
    return v

validate_b_alp classmethod

validate_b_alp(v, info)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("b_alp")
@classmethod
def validate_b_alp(cls, v, info):
    if info.data.get("br_coef_method") == 2 and v != 40:
        print("Warning: B_ALP should be around 40 when BR_COEF_METHOD is 2")
    return v

validate_zprof_break classmethod

validate_zprof_break(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("zprof_break")
@classmethod
def validate_zprof_break(cls, v):
    if v not in range(1, 7):
        raise ValueError("ZPROF_BREAK must be between 1 and 6")
    return v

validate_bc_break classmethod

validate_bc_break(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("bc_break")
@classmethod
def validate_bc_break(cls, v):
    if v not in [0, 1]:
        raise ValueError("BC_BREAK must be either 0 or 1")
    return v

validate_iroller classmethod

validate_iroller(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("iroller")
@classmethod
def validate_iroller(cls, v):
    if v not in [0, 1]:
        raise ValueError("IROLLER must be either 0 or 1")
    return v

validate_alprol classmethod

validate_alprol(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("alprol")
@classmethod
def validate_alprol(cls, v):
    if not 0 <= v <= 1:
        raise ValueError("ALPROL must be between 0 and 1")
    return v

validate_meveg classmethod

validate_meveg(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("meveg")
@classmethod
def validate_meveg(cls, v):
    if v not in [0, 1]:
        raise ValueError("MEVEG must be either 0 or 1")
    return v

validate_mesds classmethod

validate_mesds(v, info)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("mesds")
@classmethod
def validate_mesds(cls, v, info):
    if v != info.data.get("mesin"):
        raise ValueError("MESDS should have the same value as MESIN")
    return v

validate_mestr classmethod

validate_mestr(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("mestr")
@classmethod
def validate_mestr(cls, v):
    if v not in [0, 1, 2]:
        raise ValueError("MESTR must be 0, 1, or 2")
    return v

Grid

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/wwminput.py
class Grid(NamelistBaseModel):
    lcird: Optional[bool] = Field(
        True,
        description="Flag to indicate if a full circle in directional space is used. If True, MINDIR and MAXDIR are ignored.",
    )
    lstag: Optional[bool] = Field(
        False,
        description="Flag to stagger directional bins with a half Dtheta. Can only be True for regular grids to avoid characteristic line aligning with grid line.",
    )
    mindir: Optional[float] = Field(
        0.0,
        description="Minimum direction for simulation in degrees (nautical convention; 0: from N; 90: from E). Not used if LCIRD is True.",
    )
    maxdir: Optional[float] = Field(
        360.0,
        description="Maximum direction for simulation in degrees. May be less than MINDIR. Not used if LCIRD is True.",
    )
    mdc: Optional[int] = Field(24, description="Number of directional bins")
    frlow: Optional[float] = Field(
        0.04,
        description="Low frequency limit of the discrete wave period in Hz (1/period)",
    )
    frhigh: Optional[float] = Field(
        1.0, description="High frequency limit of the discrete wave period in Hz"
    )
    msc: Optional[int] = Field(24, description="Number of frequency bins")
    filegrid: Optional[str] = Field(
        "hgrid_WWM.gr3",
        description="Name of the grid file. Should be 'hgridi_WWM.gr3' if IGRIDTYPE is 3 (SCHISM)",
    )
    igridtype: Optional[int] = Field(
        3,
        description="Grid type used. 1: XFN system.dat, 2: WWM-PERIODIC, 3: SCHISM, 4: old WWM type",
    )
    lslop: Optional[bool] = Field(
        False, description="Flag to enable bottom slope limiter"
    )
    slmax: Optional[float] = Field(
        0.2, description="Maximum allowed bottom slope when LSLOP is True"
    )
    lvar1d: Optional[bool] = Field(
        False,
        description="Flag to use variable dx in 1D mode. Not used with SCHISM (IGRIDTYPE = 3)",
    )
    loptsig: Optional[bool] = Field(
        False,
        description="Flag to use optimal distributions of frequencies in spectral space (fi+1 = fi * 1.1)",
    )

    @field_validator("lcird")
    def validate_lcird(cls, v):
        return v

    @field_validator("lstag")
    def validate_lstag(cls, v, info):
        if v and info.data.get("igridtype") != 1:
            raise ValueError("LSTAG can only be True for regular grids (IGRIDTYPE = 1)")
        return v

    @field_validator("mindir")
    def validate_mindir(cls, v):
        if not 0 <= v < 360:
            raise ValueError("MINDIR must be between 0 and 360 degrees")
        return v

    @field_validator("maxdir")
    def validate_maxdir(cls, v):
        if not 0 <= v <= 360:
            raise ValueError("MAXDIR must be between 0 and 360 degrees")
        return v

    @field_validator("mdc")
    def validate_mdc(cls, v):
        if v <= 0:
            raise ValueError("MDC must be a positive integer")
        return v

    @field_validator("frlow")
    def validate_frlow(cls, v):
        if v <= 0:
            raise ValueError("FRLOW must be a positive number")
        return v

    @field_validator("frhigh")
    def validate_frhigh(cls, v, info):
        if v <= info.data.get("frlow", 0):
            raise ValueError("FRHIGH must be greater than FRLOW")
        return v

    @field_validator("msc")
    def validate_msc(cls, v):
        if v <= 0:
            raise ValueError("MSC must be a positive integer")
        return v

    @field_validator("filegrid")
    def validate_filegrid(cls, v):
        if not v.strip():
            raise ValueError("FILEGRID must not be empty")
        return v

    @field_validator("igridtype")
    def validate_igridtype(cls, v):
        if v not in [1, 2, 3, 4]:
            raise ValueError("IGRIDTYPE must be 1, 2, 3, or 4")
        return v

    @field_validator("lslop")
    def validate_lslop(cls, v):
        return v

    @field_validator("slmax")
    def validate_slmax(cls, v):
        if v <= 0:
            raise ValueError("SLMAX must be a positive number")
        return v

    @field_validator("lvar1d")
    def validate_lvar1d(cls, v, info):
        if v and info.data.get("igridtype") == 3:
            raise ValueError("LVAR1D cannot be True when IGRIDTYPE is 3 (SCHISM)")
        return v

    @field_validator("loptsig")
    def validate_loptsig(cls, v):
        return v

Attributes

lcird class-attribute instance-attribute

lcird: Optional[bool] = Field(True, description='Flag to indicate if a full circle in directional space is used. If True, MINDIR and MAXDIR are ignored.')

lstag class-attribute instance-attribute

lstag: Optional[bool] = Field(False, description='Flag to stagger directional bins with a half Dtheta. Can only be True for regular grids to avoid characteristic line aligning with grid line.')

mindir class-attribute instance-attribute

mindir: Optional[float] = Field(0.0, description='Minimum direction for simulation in degrees (nautical convention; 0: from N; 90: from E). Not used if LCIRD is True.')

maxdir class-attribute instance-attribute

maxdir: Optional[float] = Field(360.0, description='Maximum direction for simulation in degrees. May be less than MINDIR. Not used if LCIRD is True.')

mdc class-attribute instance-attribute

mdc: Optional[int] = Field(24, description='Number of directional bins')

frlow class-attribute instance-attribute

frlow: Optional[float] = Field(0.04, description='Low frequency limit of the discrete wave period in Hz (1/period)')

frhigh class-attribute instance-attribute

frhigh: Optional[float] = Field(1.0, description='High frequency limit of the discrete wave period in Hz')

msc class-attribute instance-attribute

msc: Optional[int] = Field(24, description='Number of frequency bins')

filegrid class-attribute instance-attribute

filegrid: Optional[str] = Field('hgrid_WWM.gr3', description="Name of the grid file. Should be 'hgridi_WWM.gr3' if IGRIDTYPE is 3 (SCHISM)")

igridtype class-attribute instance-attribute

igridtype: Optional[int] = Field(3, description='Grid type used. 1: XFN system.dat, 2: WWM-PERIODIC, 3: SCHISM, 4: old WWM type')

lslop class-attribute instance-attribute

lslop: Optional[bool] = Field(False, description='Flag to enable bottom slope limiter')

slmax class-attribute instance-attribute

slmax: Optional[float] = Field(0.2, description='Maximum allowed bottom slope when LSLOP is True')

lvar1d class-attribute instance-attribute

lvar1d: Optional[bool] = Field(False, description='Flag to use variable dx in 1D mode. Not used with SCHISM (IGRIDTYPE = 3)')

loptsig class-attribute instance-attribute

loptsig: Optional[bool] = Field(False, description='Flag to use optimal distributions of frequencies in spectral space (fi+1 = fi * 1.1)')

Functions

validate_lcird

validate_lcird(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("lcird")
def validate_lcird(cls, v):
    return v

validate_lstag

validate_lstag(v, info)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("lstag")
def validate_lstag(cls, v, info):
    if v and info.data.get("igridtype") != 1:
        raise ValueError("LSTAG can only be True for regular grids (IGRIDTYPE = 1)")
    return v

validate_mindir

validate_mindir(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("mindir")
def validate_mindir(cls, v):
    if not 0 <= v < 360:
        raise ValueError("MINDIR must be between 0 and 360 degrees")
    return v

validate_maxdir

validate_maxdir(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("maxdir")
def validate_maxdir(cls, v):
    if not 0 <= v <= 360:
        raise ValueError("MAXDIR must be between 0 and 360 degrees")
    return v

validate_mdc

validate_mdc(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("mdc")
def validate_mdc(cls, v):
    if v <= 0:
        raise ValueError("MDC must be a positive integer")
    return v

validate_frlow

validate_frlow(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("frlow")
def validate_frlow(cls, v):
    if v <= 0:
        raise ValueError("FRLOW must be a positive number")
    return v

validate_frhigh

validate_frhigh(v, info)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("frhigh")
def validate_frhigh(cls, v, info):
    if v <= info.data.get("frlow", 0):
        raise ValueError("FRHIGH must be greater than FRLOW")
    return v

validate_msc

validate_msc(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("msc")
def validate_msc(cls, v):
    if v <= 0:
        raise ValueError("MSC must be a positive integer")
    return v

validate_filegrid

validate_filegrid(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("filegrid")
def validate_filegrid(cls, v):
    if not v.strip():
        raise ValueError("FILEGRID must not be empty")
    return v

validate_igridtype

validate_igridtype(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("igridtype")
def validate_igridtype(cls, v):
    if v not in [1, 2, 3, 4]:
        raise ValueError("IGRIDTYPE must be 1, 2, 3, or 4")
    return v

validate_lslop

validate_lslop(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("lslop")
def validate_lslop(cls, v):
    return v

validate_slmax

validate_slmax(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("slmax")
def validate_slmax(cls, v):
    if v <= 0:
        raise ValueError("SLMAX must be a positive number")
    return v

validate_lvar1d

validate_lvar1d(v, info)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("lvar1d")
def validate_lvar1d(cls, v, info):
    if v and info.data.get("igridtype") == 3:
        raise ValueError("LVAR1D cannot be True when IGRIDTYPE is 3 (SCHISM)")
    return v

validate_loptsig

validate_loptsig(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("loptsig")
def validate_loptsig(cls, v):
    return v

History

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/wwminput.py
1370
1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398
1399
1400
1401
1402
1403
1404
1405
1406
1407
1408
1409
1410
1411
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497
1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
1520
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530
1531
1532
1533
1534
1535
1536
1537
1538
1539
1540
1541
1542
1543
1544
1545
1546
1547
1548
1549
1550
1551
1552
1553
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659
1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703
1704
1705
1706
1707
1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725
1726
1727
1728
1729
1730
1731
1732
1733
1734
1735
1736
1737
1738
1739
1740
1741
1742
1743
1744
1745
1746
1747
1748
1749
1750
1751
1752
1753
1754
1755
1756
1757
1758
1759
1760
1761
1762
1763
1764
1765
1766
1767
1768
1769
1770
1771
1772
1773
1774
1775
1776
1777
1778
1779
1780
1781
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1794
1795
1796
1797
1798
1799
1800
1801
1802
1803
1804
1805
1806
1807
1808
1809
1810
1811
1812
1813
1814
1815
1816
class History(NamelistBaseModel):
    begtc: Optional[str] = Field(
        "20200101.000000",
        description="Start output time in 'yyyymmdd.hhmmss' format. Must fit within the simulation time, otherwise no output. Defaults to PROC%BEGTC if not specified.",
    )
    deltc: Optional[int] = Field(
        3600,
        description="Time step for output in seconds. If smaller than simulation time step, the latter is used. Used for better 1D and 2D spectra analysis.",
    )
    unitc: Optional[str] = Field(
        "SEC",
        description="Unit of time for DELTC. Currently only supports 'SEC' for seconds.",
    )
    endtc: Optional[str] = Field(
        "20200201.000000",
        description="Stop time for output in 'yyyymmdd.hhmmss' format. Defaults to PROC%ENDC if not specified.",
    )
    definetc: Optional[int] = Field(
        -1,
        description="Time scoop (in seconds) for history files. If negative or unset, only one file is generated. For example, 86400 creates daily output files.",
    )
    outstyle: Optional[str] = Field(
        "NO",
        description="Output option. 'NO' for no output, 'NC' for netCDF, 'XFN' for XFN (default), 'SHP' for DARKO SHP output.",
    )
    multipleout: Optional[int] = Field(
        0,
        description="Output file configuration. 0 for single netCDF file using MPI_reduce (default), 1 for separate netCDF files for each process.",
    )
    use_single_out: Optional[bool] = Field(
        True,
        description="Use single precision in the output of model variables. True by default.",
    )
    paramwrite: Optional[bool] = Field(
        True,
        description="Write the physical parametrization and chosen numerical method in the netCDF file. True by default.",
    )
    gridwrite: Optional[bool] = Field(
        True, description="Write the grid in the netCDF history file. True by default."
    )
    printmma: Optional[bool] = Field(
        False,
        description="Print minimum, maximum and average value of statistics during runtime. Requires MPI_REDUCE. False by default.",
    )
    fileout: Optional[str] = Field(
        "wwm_hist.nc", description="Name of the output file."
    )
    hs: Optional[bool] = Field(True, description="Output significant wave height.")
    tm01: Optional[bool] = Field(True, description="Output mean period.")
    tm02: Optional[bool] = Field(True, description="Output zero-crossing mean period.")
    klm: Optional[bool] = Field(False, description="Output mean wave number.")
    wlm: Optional[bool] = Field(False, description="Output mean wave length.")
    etotc: Optional[bool] = Field(False, description="Output variable ETOTC.")
    etots: Optional[bool] = Field(False, description="Output variable ETOTS.")
    dm: Optional[bool] = Field(True, description="Output mean wave direction.")
    dspr: Optional[bool] = Field(True, description="Output directional spreading.")
    tppd: Optional[bool] = Field(
        False,
        description="Output direction of the peak (check source code for details).",
    )
    tpp: Optional[bool] = Field(True, description="Output peak period.")
    cpp: Optional[bool] = Field(False, description="Output peak phase velocity.")
    wnpp: Optional[bool] = Field(False, description="Output peak wave number.")
    cgpp: Optional[bool] = Field(False, description="Output peak group speed.")
    kpp: Optional[bool] = Field(False, description="Output peak wave number.")
    lpp: Optional[bool] = Field(False, description="Output peak wave length.")
    peakd: Optional[bool] = Field(True, description="Output peak direction.")
    peakdspr: Optional[bool] = Field(
        True, description="Output peak directional spreading."
    )
    dpeak: Optional[bool] = Field(False, description="Output peak direction.")
    ubot: Optional[bool] = Field(False, description="Output bottom excursion velocity.")
    orbital: Optional[bool] = Field(
        False, description="Output bottom orbital velocity."
    )
    botexper: Optional[bool] = Field(False, description="Output bottom excursion.")
    tmbot: Optional[bool] = Field(False, description="Output bottom period.")
    ursell: Optional[bool] = Field(False, description="Output Ursell number.")
    ufric: Optional[bool] = Field(False, description="Output air friction velocity.")
    z0: Optional[bool] = Field(False, description="Output air roughness length.")
    alpha_ch: Optional[bool] = Field(
        False, description="Output Charnock coefficient for air."
    )
    windx: Optional[bool] = Field(False, description="Output wind in X direction.")
    windy: Optional[bool] = Field(False, description="Output wind in Y direction.")
    cd: Optional[bool] = Field(False, description="Output drag coefficient.")
    currtx: Optional[bool] = Field(False, description="Output current in X direction.")
    currty: Optional[bool] = Field(False, description="Output current in Y direction.")
    watlev: Optional[bool] = Field(False, description="Output water level.")
    watlevold: Optional[bool] = Field(
        False, description="Output water level at previous time step."
    )
    depdt: Optional[bool] = Field(
        False, description="Output change of water level in time."
    )
    dep: Optional[bool] = Field(False, description="Output depth.")
    tauw: Optional[bool] = Field(
        True, description="Output surface stress from the wave."
    )
    tauhf: Optional[bool] = Field(
        False, description="Output high frequency surface stress."
    )
    tautot: Optional[bool] = Field(True, description="Output total surface stress.")
    stokessurfx: Optional[bool] = Field(
        True, description="Output surface Stokes drift in X direction."
    )
    stokessurfy: Optional[bool] = Field(
        True, description="Output surface Stokes drift in Y direction."
    )
    stokesbarox: Optional[bool] = Field(
        False, description="Output barotropic Stokes drift in X direction."
    )
    stokesbaroy: Optional[bool] = Field(
        False, description="Output barotropic Stokes drift in Y direction."
    )
    rsxx: Optional[bool] = Field(False, description="Output RSXX potential of LH.")
    rsxy: Optional[bool] = Field(False, description="Output RSXY potential of LH.")
    rsyy: Optional[bool] = Field(False, description="Output RSYY potential of LH.")
    cfl1: Optional[bool] = Field(False, description="Output CFL number 1.")
    cfl2: Optional[bool] = Field(False, description="Output CFL number 2.")
    cfl3: Optional[bool] = Field(False, description="Output CFL number 3.")

    @field_validator("begtc")
    @classmethod
    def validate_begtc(cls, v):
        try:
            datetime.strptime(v, "%Y%m%d.%H%M%S")
            return v
        except ValueError:
            raise ValueError("Invalid date format. Use yyyymmdd.hhmmss")

    @field_validator("deltc")
    @classmethod
    def validate_deltc(cls, v):
        if v <= 0:
            raise ValueError("DELTC must be positive")
        return v

    @field_validator("unitc")
    @classmethod
    def validate_unitc(cls, v):
        if v.upper() != "SEC":
            raise ValueError("UNITC must be SEC")
        return v.upper()

    @field_validator("endtc")
    @classmethod
    def validate_endtc(cls, v):
        try:
            datetime.strptime(v, "%Y%m%d.%H%M%S")
            return v
        except ValueError:
            raise ValueError("Invalid date format. Use yyyymmdd.hhmmss")

    @field_validator("definetc")
    @classmethod
    def validate_definetc(cls, v):
        if not isinstance(v, int):
            raise ValueError("DEFINETC must be an integer")
        return v

    @field_validator("outstyle")
    @classmethod
    def validate_outstyle(cls, v):
        valid_options = ["NO", "NC", "XFN", "SHP"]
        if v.upper() not in valid_options:
            raise ValueError(f"OUTSTYLE must be one of {valid_options}")
        return v.upper()

    @field_validator("multipleout")
    @classmethod
    def validate_multipleout(cls, v):
        if v not in [0, 1]:
            raise ValueError("MULTIPLEOUT must be 0 or 1")
        return v

    @field_validator("use_single_out")
    @classmethod
    def validate_use_single_out(cls, v):
        return v

    @field_validator("paramwrite")
    @classmethod
    def validate_paramwrite(cls, v):
        return v

    @field_validator("gridwrite")
    @classmethod
    def validate_gridwrite(cls, v):
        return v

    @field_validator("printmma")
    @classmethod
    def validate_printmma(cls, v):
        return v

    @field_validator("fileout")
    @classmethod
    def validate_fileout(cls, v):
        if not v.endswith(".nc"):
            raise ValueError("FILEOUT must have .nc extension")
        return v

    @field_validator("hs")
    @classmethod
    def validate_hs(cls, v):
        return v

    @field_validator("tm01")
    @classmethod
    def validate_tm01(cls, v):
        return v

    @field_validator("tm02")
    @classmethod
    def validate_tm02(cls, v):
        return v

    @field_validator("klm")
    @classmethod
    def validate_klm(cls, v):
        return v

    @field_validator("wlm")
    @classmethod
    def validate_wlm(cls, v):
        return v

    @field_validator("etotc")
    @classmethod
    def validate_etotc(cls, v):
        return v

    @field_validator("etots")
    @classmethod
    def validate_etots(cls, v):
        return v

    @field_validator("dm")
    @classmethod
    def validate_dm(cls, v):
        return v

    @field_validator("dspr")
    @classmethod
    def validate_dspr(cls, v):
        return v

    @field_validator("tppd")
    @classmethod
    def validate_tppd(cls, v):
        return v

    @field_validator("tpp")
    @classmethod
    def validate_tpp(cls, v):
        return v

    @field_validator("cpp")
    @classmethod
    def validate_cpp(cls, v):
        return v

    @field_validator("wnpp")
    @classmethod
    def validate_wnpp(cls, v):
        return v

    @field_validator("cgpp")
    @classmethod
    def validate_cgpp(cls, v):
        return v

    @field_validator("kpp")
    @classmethod
    def validate_kpp(cls, v):
        return v

    @field_validator("lpp")
    @classmethod
    def validate_lpp(cls, v):
        return v

    @field_validator("peakd")
    @classmethod
    def validate_peakd(cls, v):
        return v

    @field_validator("peakdspr")
    @classmethod
    def validate_peakdspr(cls, v):
        return v

    @field_validator("dpeak")
    @classmethod
    def validate_dpeak(cls, v):
        return v

    @field_validator("ubot")
    @classmethod
    def validate_ubot(cls, v):
        return v

    @field_validator("orbital")
    @classmethod
    def validate_orbital(cls, v):
        return v

    @field_validator("botexper")
    @classmethod
    def validate_botexper(cls, v):
        return v

    @field_validator("tmbot")
    @classmethod
    def validate_tmbot(cls, v):
        return v

    @field_validator("ursell")
    @classmethod
    def validate_ursell(cls, v):
        return v

    @field_validator("ufric")
    @classmethod
    def validate_ufric(cls, v):
        return v

    @field_validator("z0")
    @classmethod
    def validate_z0(cls, v):
        return v

    @field_validator("alpha_ch")
    @classmethod
    def validate_alpha_ch(cls, v):
        return v

    @field_validator("windx")
    @classmethod
    def validate_windx(cls, v):
        return v

    @field_validator("windy")
    @classmethod
    def validate_windy(cls, v):
        return v

    @field_validator("cd")
    @classmethod
    def validate_cd(cls, v):
        return v

    @field_validator("currtx")
    @classmethod
    def validate_currtx(cls, v):
        return v

    @field_validator("currty")
    @classmethod
    def validate_currty(cls, v):
        return v

    @field_validator("watlev")
    @classmethod
    def validate_watlev(cls, v):
        return v

    @field_validator("watlevold")
    @classmethod
    def validate_watlevold(cls, v):
        return v

    @field_validator("depdt")
    @classmethod
    def validate_depdt(cls, v):
        return v

    @field_validator("dep")
    @classmethod
    def validate_dep(cls, v):
        return v

    @field_validator("tauw")
    @classmethod
    def validate_tauw(cls, v):
        return v

    @field_validator("tauhf")
    @classmethod
    def validate_tauhf(cls, v):
        return v

    @field_validator("tautot")
    @classmethod
    def validate_tautot(cls, v):
        return v

    @field_validator("stokessurfx")
    @classmethod
    def validate_stokessurfx(cls, v):
        return v

    @field_validator("stokessurfy")
    @classmethod
    def validate_stokessurfy(cls, v):
        return v

    @field_validator("stokesbarox")
    @classmethod
    def validate_stokesbarox(cls, v):
        return v

    @field_validator("stokesbaroy")
    @classmethod
    def validate_stokesbaroy(cls, v):
        return v

    @field_validator("rsxx")
    @classmethod
    def validate_rsxx(cls, v):
        return v

    @field_validator("rsxy")
    @classmethod
    def validate_rsxy(cls, v):
        return v

    @field_validator("rsyy")
    @classmethod
    def validate_rsyy(cls, v):
        return v

    @field_validator("cfl1")
    @classmethod
    def validate_cfl1(cls, v):
        return v

    @field_validator("cfl2")
    @classmethod
    def validate_cfl2(cls, v):
        return v

    @field_validator("cfl3")
    @classmethod
    def validate_cfl3(cls, v):
        return v

Attributes

begtc class-attribute instance-attribute

begtc: Optional[str] = Field('20200101.000000', description="Start output time in 'yyyymmdd.hhmmss' format. Must fit within the simulation time, otherwise no output. Defaults to PROC%BEGTC if not specified.")

deltc class-attribute instance-attribute

deltc: Optional[int] = Field(3600, description='Time step for output in seconds. If smaller than simulation time step, the latter is used. Used for better 1D and 2D spectra analysis.')

unitc class-attribute instance-attribute

unitc: Optional[str] = Field('SEC', description="Unit of time for DELTC. Currently only supports 'SEC' for seconds.")

endtc class-attribute instance-attribute

endtc: Optional[str] = Field('20200201.000000', description="Stop time for output in 'yyyymmdd.hhmmss' format. Defaults to PROC%ENDC if not specified.")

definetc class-attribute instance-attribute

definetc: Optional[int] = Field(-1, description='Time scoop (in seconds) for history files. If negative or unset, only one file is generated. For example, 86400 creates daily output files.')

outstyle class-attribute instance-attribute

outstyle: Optional[str] = Field('NO', description="Output option. 'NO' for no output, 'NC' for netCDF, 'XFN' for XFN (default), 'SHP' for DARKO SHP output.")

multipleout class-attribute instance-attribute

multipleout: Optional[int] = Field(0, description='Output file configuration. 0 for single netCDF file using MPI_reduce (default), 1 for separate netCDF files for each process.')

use_single_out class-attribute instance-attribute

use_single_out: Optional[bool] = Field(True, description='Use single precision in the output of model variables. True by default.')

paramwrite class-attribute instance-attribute

paramwrite: Optional[bool] = Field(True, description='Write the physical parametrization and chosen numerical method in the netCDF file. True by default.')

gridwrite class-attribute instance-attribute

gridwrite: Optional[bool] = Field(True, description='Write the grid in the netCDF history file. True by default.')

printmma class-attribute instance-attribute

printmma: Optional[bool] = Field(False, description='Print minimum, maximum and average value of statistics during runtime. Requires MPI_REDUCE. False by default.')

fileout class-attribute instance-attribute

fileout: Optional[str] = Field('wwm_hist.nc', description='Name of the output file.')

hs class-attribute instance-attribute

hs: Optional[bool] = Field(True, description='Output significant wave height.')

tm01 class-attribute instance-attribute

tm01: Optional[bool] = Field(True, description='Output mean period.')

tm02 class-attribute instance-attribute

tm02: Optional[bool] = Field(True, description='Output zero-crossing mean period.')

klm class-attribute instance-attribute

klm: Optional[bool] = Field(False, description='Output mean wave number.')

wlm class-attribute instance-attribute

wlm: Optional[bool] = Field(False, description='Output mean wave length.')

etotc class-attribute instance-attribute

etotc: Optional[bool] = Field(False, description='Output variable ETOTC.')

etots class-attribute instance-attribute

etots: Optional[bool] = Field(False, description='Output variable ETOTS.')

dm class-attribute instance-attribute

dm: Optional[bool] = Field(True, description='Output mean wave direction.')

dspr class-attribute instance-attribute

dspr: Optional[bool] = Field(True, description='Output directional spreading.')

tppd class-attribute instance-attribute

tppd: Optional[bool] = Field(False, description='Output direction of the peak (check source code for details).')

tpp class-attribute instance-attribute

tpp: Optional[bool] = Field(True, description='Output peak period.')

cpp class-attribute instance-attribute

cpp: Optional[bool] = Field(False, description='Output peak phase velocity.')

wnpp class-attribute instance-attribute

wnpp: Optional[bool] = Field(False, description='Output peak wave number.')

cgpp class-attribute instance-attribute

cgpp: Optional[bool] = Field(False, description='Output peak group speed.')

kpp class-attribute instance-attribute

kpp: Optional[bool] = Field(False, description='Output peak wave number.')

lpp class-attribute instance-attribute

lpp: Optional[bool] = Field(False, description='Output peak wave length.')

peakd class-attribute instance-attribute

peakd: Optional[bool] = Field(True, description='Output peak direction.')

peakdspr class-attribute instance-attribute

peakdspr: Optional[bool] = Field(True, description='Output peak directional spreading.')

dpeak class-attribute instance-attribute

dpeak: Optional[bool] = Field(False, description='Output peak direction.')

ubot class-attribute instance-attribute

ubot: Optional[bool] = Field(False, description='Output bottom excursion velocity.')

orbital class-attribute instance-attribute

orbital: Optional[bool] = Field(False, description='Output bottom orbital velocity.')

botexper class-attribute instance-attribute

botexper: Optional[bool] = Field(False, description='Output bottom excursion.')

tmbot class-attribute instance-attribute

tmbot: Optional[bool] = Field(False, description='Output bottom period.')

ursell class-attribute instance-attribute

ursell: Optional[bool] = Field(False, description='Output Ursell number.')

ufric class-attribute instance-attribute

ufric: Optional[bool] = Field(False, description='Output air friction velocity.')

z0 class-attribute instance-attribute

z0: Optional[bool] = Field(False, description='Output air roughness length.')

alpha_ch class-attribute instance-attribute

alpha_ch: Optional[bool] = Field(False, description='Output Charnock coefficient for air.')

windx class-attribute instance-attribute

windx: Optional[bool] = Field(False, description='Output wind in X direction.')

windy class-attribute instance-attribute

windy: Optional[bool] = Field(False, description='Output wind in Y direction.')

cd class-attribute instance-attribute

cd: Optional[bool] = Field(False, description='Output drag coefficient.')

currtx class-attribute instance-attribute

currtx: Optional[bool] = Field(False, description='Output current in X direction.')

currty class-attribute instance-attribute

currty: Optional[bool] = Field(False, description='Output current in Y direction.')

watlev class-attribute instance-attribute

watlev: Optional[bool] = Field(False, description='Output water level.')

watlevold class-attribute instance-attribute

watlevold: Optional[bool] = Field(False, description='Output water level at previous time step.')

depdt class-attribute instance-attribute

depdt: Optional[bool] = Field(False, description='Output change of water level in time.')

dep class-attribute instance-attribute

dep: Optional[bool] = Field(False, description='Output depth.')

tauw class-attribute instance-attribute

tauw: Optional[bool] = Field(True, description='Output surface stress from the wave.')

tauhf class-attribute instance-attribute

tauhf: Optional[bool] = Field(False, description='Output high frequency surface stress.')

tautot class-attribute instance-attribute

tautot: Optional[bool] = Field(True, description='Output total surface stress.')

stokessurfx class-attribute instance-attribute

stokessurfx: Optional[bool] = Field(True, description='Output surface Stokes drift in X direction.')

stokessurfy class-attribute instance-attribute

stokessurfy: Optional[bool] = Field(True, description='Output surface Stokes drift in Y direction.')

stokesbarox class-attribute instance-attribute

stokesbarox: Optional[bool] = Field(False, description='Output barotropic Stokes drift in X direction.')

stokesbaroy class-attribute instance-attribute

stokesbaroy: Optional[bool] = Field(False, description='Output barotropic Stokes drift in Y direction.')

rsxx class-attribute instance-attribute

rsxx: Optional[bool] = Field(False, description='Output RSXX potential of LH.')

rsxy class-attribute instance-attribute

rsxy: Optional[bool] = Field(False, description='Output RSXY potential of LH.')

rsyy class-attribute instance-attribute

rsyy: Optional[bool] = Field(False, description='Output RSYY potential of LH.')

cfl1 class-attribute instance-attribute

cfl1: Optional[bool] = Field(False, description='Output CFL number 1.')

cfl2 class-attribute instance-attribute

cfl2: Optional[bool] = Field(False, description='Output CFL number 2.')

cfl3 class-attribute instance-attribute

cfl3: Optional[bool] = Field(False, description='Output CFL number 3.')

Functions

validate_begtc classmethod

validate_begtc(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("begtc")
@classmethod
def validate_begtc(cls, v):
    try:
        datetime.strptime(v, "%Y%m%d.%H%M%S")
        return v
    except ValueError:
        raise ValueError("Invalid date format. Use yyyymmdd.hhmmss")

validate_deltc classmethod

validate_deltc(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("deltc")
@classmethod
def validate_deltc(cls, v):
    if v <= 0:
        raise ValueError("DELTC must be positive")
    return v

validate_unitc classmethod

validate_unitc(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("unitc")
@classmethod
def validate_unitc(cls, v):
    if v.upper() != "SEC":
        raise ValueError("UNITC must be SEC")
    return v.upper()

validate_endtc classmethod

validate_endtc(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("endtc")
@classmethod
def validate_endtc(cls, v):
    try:
        datetime.strptime(v, "%Y%m%d.%H%M%S")
        return v
    except ValueError:
        raise ValueError("Invalid date format. Use yyyymmdd.hhmmss")

validate_definetc classmethod

validate_definetc(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("definetc")
@classmethod
def validate_definetc(cls, v):
    if not isinstance(v, int):
        raise ValueError("DEFINETC must be an integer")
    return v

validate_outstyle classmethod

validate_outstyle(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("outstyle")
@classmethod
def validate_outstyle(cls, v):
    valid_options = ["NO", "NC", "XFN", "SHP"]
    if v.upper() not in valid_options:
        raise ValueError(f"OUTSTYLE must be one of {valid_options}")
    return v.upper()

validate_multipleout classmethod

validate_multipleout(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("multipleout")
@classmethod
def validate_multipleout(cls, v):
    if v not in [0, 1]:
        raise ValueError("MULTIPLEOUT must be 0 or 1")
    return v

validate_use_single_out classmethod

validate_use_single_out(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("use_single_out")
@classmethod
def validate_use_single_out(cls, v):
    return v

validate_paramwrite classmethod

validate_paramwrite(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("paramwrite")
@classmethod
def validate_paramwrite(cls, v):
    return v

validate_gridwrite classmethod

validate_gridwrite(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("gridwrite")
@classmethod
def validate_gridwrite(cls, v):
    return v

validate_printmma classmethod

validate_printmma(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("printmma")
@classmethod
def validate_printmma(cls, v):
    return v

validate_fileout classmethod

validate_fileout(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("fileout")
@classmethod
def validate_fileout(cls, v):
    if not v.endswith(".nc"):
        raise ValueError("FILEOUT must have .nc extension")
    return v

validate_hs classmethod

validate_hs(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("hs")
@classmethod
def validate_hs(cls, v):
    return v

validate_tm01 classmethod

validate_tm01(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("tm01")
@classmethod
def validate_tm01(cls, v):
    return v

validate_tm02 classmethod

validate_tm02(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("tm02")
@classmethod
def validate_tm02(cls, v):
    return v

validate_klm classmethod

validate_klm(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("klm")
@classmethod
def validate_klm(cls, v):
    return v

validate_wlm classmethod

validate_wlm(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("wlm")
@classmethod
def validate_wlm(cls, v):
    return v

validate_etotc classmethod

validate_etotc(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("etotc")
@classmethod
def validate_etotc(cls, v):
    return v

validate_etots classmethod

validate_etots(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("etots")
@classmethod
def validate_etots(cls, v):
    return v

validate_dm classmethod

validate_dm(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("dm")
@classmethod
def validate_dm(cls, v):
    return v

validate_dspr classmethod

validate_dspr(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("dspr")
@classmethod
def validate_dspr(cls, v):
    return v

validate_tppd classmethod

validate_tppd(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("tppd")
@classmethod
def validate_tppd(cls, v):
    return v

validate_tpp classmethod

validate_tpp(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("tpp")
@classmethod
def validate_tpp(cls, v):
    return v

validate_cpp classmethod

validate_cpp(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("cpp")
@classmethod
def validate_cpp(cls, v):
    return v

validate_wnpp classmethod

validate_wnpp(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("wnpp")
@classmethod
def validate_wnpp(cls, v):
    return v

validate_cgpp classmethod

validate_cgpp(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("cgpp")
@classmethod
def validate_cgpp(cls, v):
    return v

validate_kpp classmethod

validate_kpp(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("kpp")
@classmethod
def validate_kpp(cls, v):
    return v

validate_lpp classmethod

validate_lpp(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("lpp")
@classmethod
def validate_lpp(cls, v):
    return v

validate_peakd classmethod

validate_peakd(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("peakd")
@classmethod
def validate_peakd(cls, v):
    return v

validate_peakdspr classmethod

validate_peakdspr(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("peakdspr")
@classmethod
def validate_peakdspr(cls, v):
    return v

validate_dpeak classmethod

validate_dpeak(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("dpeak")
@classmethod
def validate_dpeak(cls, v):
    return v

validate_ubot classmethod

validate_ubot(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("ubot")
@classmethod
def validate_ubot(cls, v):
    return v

validate_orbital classmethod

validate_orbital(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("orbital")
@classmethod
def validate_orbital(cls, v):
    return v

validate_botexper classmethod

validate_botexper(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("botexper")
@classmethod
def validate_botexper(cls, v):
    return v

validate_tmbot classmethod

validate_tmbot(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("tmbot")
@classmethod
def validate_tmbot(cls, v):
    return v

validate_ursell classmethod

validate_ursell(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("ursell")
@classmethod
def validate_ursell(cls, v):
    return v

validate_ufric classmethod

validate_ufric(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("ufric")
@classmethod
def validate_ufric(cls, v):
    return v

validate_z0 classmethod

validate_z0(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("z0")
@classmethod
def validate_z0(cls, v):
    return v

validate_alpha_ch classmethod

validate_alpha_ch(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("alpha_ch")
@classmethod
def validate_alpha_ch(cls, v):
    return v

validate_windx classmethod

validate_windx(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("windx")
@classmethod
def validate_windx(cls, v):
    return v

validate_windy classmethod

validate_windy(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("windy")
@classmethod
def validate_windy(cls, v):
    return v

validate_cd classmethod

validate_cd(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("cd")
@classmethod
def validate_cd(cls, v):
    return v

validate_currtx classmethod

validate_currtx(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("currtx")
@classmethod
def validate_currtx(cls, v):
    return v

validate_currty classmethod

validate_currty(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("currty")
@classmethod
def validate_currty(cls, v):
    return v

validate_watlev classmethod

validate_watlev(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("watlev")
@classmethod
def validate_watlev(cls, v):
    return v

validate_watlevold classmethod

validate_watlevold(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("watlevold")
@classmethod
def validate_watlevold(cls, v):
    return v

validate_depdt classmethod

validate_depdt(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("depdt")
@classmethod
def validate_depdt(cls, v):
    return v

validate_dep classmethod

validate_dep(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("dep")
@classmethod
def validate_dep(cls, v):
    return v

validate_tauw classmethod

validate_tauw(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("tauw")
@classmethod
def validate_tauw(cls, v):
    return v

validate_tauhf classmethod

validate_tauhf(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("tauhf")
@classmethod
def validate_tauhf(cls, v):
    return v

validate_tautot classmethod

validate_tautot(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("tautot")
@classmethod
def validate_tautot(cls, v):
    return v

validate_stokessurfx classmethod

validate_stokessurfx(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("stokessurfx")
@classmethod
def validate_stokessurfx(cls, v):
    return v

validate_stokessurfy classmethod

validate_stokessurfy(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("stokessurfy")
@classmethod
def validate_stokessurfy(cls, v):
    return v

validate_stokesbarox classmethod

validate_stokesbarox(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("stokesbarox")
@classmethod
def validate_stokesbarox(cls, v):
    return v

validate_stokesbaroy classmethod

validate_stokesbaroy(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("stokesbaroy")
@classmethod
def validate_stokesbaroy(cls, v):
    return v

validate_rsxx classmethod

validate_rsxx(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("rsxx")
@classmethod
def validate_rsxx(cls, v):
    return v

validate_rsxy classmethod

validate_rsxy(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("rsxy")
@classmethod
def validate_rsxy(cls, v):
    return v

validate_rsyy classmethod

validate_rsyy(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("rsyy")
@classmethod
def validate_rsyy(cls, v):
    return v

validate_cfl1 classmethod

validate_cfl1(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("cfl1")
@classmethod
def validate_cfl1(cls, v):
    return v

validate_cfl2 classmethod

validate_cfl2(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("cfl2")
@classmethod
def validate_cfl2(cls, v):
    return v

validate_cfl3 classmethod

validate_cfl3(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("cfl3")
@classmethod
def validate_cfl3(cls, v):
    return v

Hotfile

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/wwminput.py
class Hotfile(NamelistBaseModel):
    lhotf: Optional[bool] = Field(True, description="Write hotfile")
    filehot_out: Optional[str] = Field("wwm_hot_out.nc", description="name of output")
    begtc: Optional[str] = Field(
        "20200101.000000",
        description="Starting time of hotfile writing. With ihot!=0 in SCHISM,",
    )
    deltc: Optional[float] = Field(2678400.0, description="time between hotfile writes")
    unitc: Optional[str] = Field("SEC", description="unit used above")
    endtc: Optional[str] = Field(
        "20200201.000000",
        description="Ending time of hotfile writing (adjust with BEGTC)",
    )
    lcyclehot: Optional[bool] = Field(False, description="Applies only to netcdf")
    hotstyle_out: Optional[int] = Field(
        2, description="1: binary hotfile of data as output"
    )
    multipleout: Optional[int] = Field(
        0, description="0: hotfile in a single file (binary or netcdf)"
    )
    filehot_in: Optional[str] = Field(
        "wwm_hot_in.nc",
        description="(Full) hot file name for input (which can be copied from FILEHOT_OUT above)",
    )
    hotstyle_in: Optional[int] = Field(
        2, description="1: binary hotfile of data as input"
    )
    ihotpos_in: Optional[int] = Field(
        2, description="Position in hotfile (only for netcdf)"
    )
    multiplein: Optional[int] = Field(
        0, description="0: read hotfile from one single file"
    )

Attributes

lhotf class-attribute instance-attribute

lhotf: Optional[bool] = Field(True, description='Write hotfile')

filehot_out class-attribute instance-attribute

filehot_out: Optional[str] = Field('wwm_hot_out.nc', description='name of output')

begtc class-attribute instance-attribute

begtc: Optional[str] = Field('20200101.000000', description='Starting time of hotfile writing. With ihot!=0 in SCHISM,')

deltc class-attribute instance-attribute

deltc: Optional[float] = Field(2678400.0, description='time between hotfile writes')

unitc class-attribute instance-attribute

unitc: Optional[str] = Field('SEC', description='unit used above')

endtc class-attribute instance-attribute

endtc: Optional[str] = Field('20200201.000000', description='Ending time of hotfile writing (adjust with BEGTC)')

lcyclehot class-attribute instance-attribute

lcyclehot: Optional[bool] = Field(False, description='Applies only to netcdf')

hotstyle_out class-attribute instance-attribute

hotstyle_out: Optional[int] = Field(2, description='1: binary hotfile of data as output')

multipleout class-attribute instance-attribute

multipleout: Optional[int] = Field(0, description='0: hotfile in a single file (binary or netcdf)')

filehot_in class-attribute instance-attribute

filehot_in: Optional[str] = Field('wwm_hot_in.nc', description='(Full) hot file name for input (which can be copied from FILEHOT_OUT above)')

hotstyle_in class-attribute instance-attribute

hotstyle_in: Optional[int] = Field(2, description='1: binary hotfile of data as input')

ihotpos_in class-attribute instance-attribute

ihotpos_in: Optional[int] = Field(2, description='Position in hotfile (only for netcdf)')

multiplein class-attribute instance-attribute

multiplein: Optional[int] = Field(0, description='0: read hotfile from one single file')

Init

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/wwminput.py
class Init(NamelistBaseModel):
    lhotr: Optional[bool] = Field(
        False,
        description="Flag to indicate whether to use a hotstart file for initial conditions. If True, the model will read initial conditions from a file specified in the HOTFILE section.",
    )
    linid: Optional[bool] = Field(
        True,
        description="Flag to control the initial condition setup. If False, default initial conditions are used. If True, it allows for using external sources (e.g., WW3) as initial conditions.",
    )
    initstyle: Optional[int] = Field(
        2,
        description="Specifies the method for initializing wave conditions. 1 for Parametric Jonswap, 2 for reading from Global NETCDF files. Option 2 only works if IBOUNDFORMAT is set to 3.",
    )

    # validator to ensure thatn only one of lhotr and linid is True
    @model_validator(mode="after")
    def check_init(self):
        if self.lhotr and self.linid:
            raise ValueError("Only one of LHOTR and LINID can be True")
        return self

    @field_validator("initstyle")
    @classmethod
    def validate_initstyle(cls, v):
        if v not in [1, 2]:
            raise ValueError("initstyle must be either 1 or 2")
        return v

Attributes

lhotr class-attribute instance-attribute

lhotr: Optional[bool] = Field(False, description='Flag to indicate whether to use a hotstart file for initial conditions. If True, the model will read initial conditions from a file specified in the HOTFILE section.')

linid class-attribute instance-attribute

linid: Optional[bool] = Field(True, description='Flag to control the initial condition setup. If False, default initial conditions are used. If True, it allows for using external sources (e.g., WW3) as initial conditions.')

initstyle class-attribute instance-attribute

initstyle: Optional[int] = Field(2, description='Specifies the method for initializing wave conditions. 1 for Parametric Jonswap, 2 for reading from Global NETCDF files. Option 2 only works if IBOUNDFORMAT is set to 3.')

Functions

check_init

check_init()
Source code in rompy_schism/namelists/wwminput.py
@model_validator(mode="after")
def check_init(self):
    if self.lhotr and self.linid:
        raise ValueError("Only one of LHOTR and LINID can be True")
    return self

validate_initstyle classmethod

validate_initstyle(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("initstyle")
@classmethod
def validate_initstyle(cls, v):
    if v not in [1, 2]:
        raise ValueError("initstyle must be either 1 or 2")
    return v

Nesting

Bases: NamelistBaseModel

Empty class to fill empty nesting namelist

Source code in rompy_schism/namelists/wwminput.py
class Nesting(NamelistBaseModel):
    "Empty class to fill empty nesting namelist"

Nums

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/wwminput.py
 834
 835
 836
 837
 838
 839
 840
 841
 842
 843
 844
 845
 846
 847
 848
 849
 850
 851
 852
 853
 854
 855
 856
 857
 858
 859
 860
 861
 862
 863
 864
 865
 866
 867
 868
 869
 870
 871
 872
 873
 874
 875
 876
 877
 878
 879
 880
 881
 882
 883
 884
 885
 886
 887
 888
 889
 890
 891
 892
 893
 894
 895
 896
 897
 898
 899
 900
 901
 902
 903
 904
 905
 906
 907
 908
 909
 910
 911
 912
 913
 914
 915
 916
 917
 918
 919
 920
 921
 922
 923
 924
 925
 926
 927
 928
 929
 930
 931
 932
 933
 934
 935
 936
 937
 938
 939
 940
 941
 942
 943
 944
 945
 946
 947
 948
 949
 950
 951
 952
 953
 954
 955
 956
 957
 958
 959
 960
 961
 962
 963
 964
 965
 966
 967
 968
 969
 970
 971
 972
 973
 974
 975
 976
 977
 978
 979
 980
 981
 982
 983
 984
 985
 986
 987
 988
 989
 990
 991
 992
 993
 994
 995
 996
 997
 998
 999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
1263
1264
1265
1266
1267
1268
1269
1270
1271
1272
1273
1274
1275
1276
1277
1278
1279
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290
1291
1292
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367
class Nums(NamelistBaseModel):
    icomp: Optional[int] = Field(
        3,
        description="Controls the integration scheme for splitting and advection. 0: All explicit. 1: Implicit geographical advection. 2: Implicit advection and semi-implicit source terms. 3: Fully implicit, no splitting.",
    )
    amethod: Optional[int] = Field(
        7,
        description="Controls the advection method in geographical space. Values 0-7 represent different schemes, including explicit, implicit, and PETSc-based methods.",
    )
    smethod: Optional[int] = Field(
        6,
        description="Controls the integration method for source terms. 0: No source terms. 1-6: Various splitting and integration schemes.",
    )
    dmethod: Optional[int] = Field(
        2,
        description="Controls the numerical method in directional space. 0: No advection. 1-4: Various schemes including Crank-Nicholson, Ultimate Quickest, RK5-WENO, and Explicit FVM Upwind.",
    )
    rtheta: Optional[float] = Field(
        0.5,
        description="Weighing factor for DMETHOD = 1. Only useful for Crank Nicholson integration with CFL <= 2.",
    )
    litersplit: Optional[bool] = Field(
        False,
        description="Splitting method. True: double Strang split. False: simple split (more efficient).",
    )
    lfilterth: Optional[bool] = Field(
        False,
        description="Use a CFL filter to limit the advection velocity in directional space. Similar to WW3, but mostly unused as WWMII is always stable.",
    )
    maxcflth: Optional[float] = Field(
        1.0,
        description="Maximum CFL number in Theta space. Used only if LFILTERTH=True.",
    )
    fmethod: Optional[int] = Field(
        1,
        description="Controls the numerical method in frequency space. 0: No advection. 1: Ultimate Quickest as in WW3 (best).",
    )
    lfiltersig: Optional[bool] = Field(
        False,
        description="Limit the advection velocity in frequency space. Usually False.",
    )
    maxcflsig: Optional[float] = Field(
        1.0,
        description="Maximum CFL number in frequency space. Used only if LFILTERSIG=True.",
    )
    llimt: Optional[bool] = Field(
        True, description="Switch on/off Action limiter. Must mostly be turned on."
    )
    melim: Optional[int] = Field(
        1,
        description="Formulation for the action limiter. 1: WAM group (1988). 2: Hersbach Janssen (1999). 3: For Cycle 4 formulation.",
    )
    limfak: Optional[float] = Field(
        0.1,
        description="Proportionality coefficient for the action limiter. MAX_DAC_DT = Limfak * Limiter.",
    )
    ldifr: Optional[bool] = Field(
        False,
        description="Use phase decoupled diffraction approximation. Usually True; if crash, use False.",
    )
    idiffr: Optional[int] = Field(
        1,
        description="Extended WAE accounting for higher order effects. 1: Holthuijsen et al. 2: Liau et al. 3: Toledo et al.",
    )
    lconv: Optional[bool] = Field(
        False,
        description="Estimate convergence criteria and write to disk (quasi-steady - qstea.out).",
    )
    lcfl: Optional[bool] = Field(
        False, description="Write out CFL numbers. Use False to save time."
    )
    nqsiter: Optional[int] = Field(
        1,
        description="Number of quasi-steady (Q-S) sub-divisions within each WWM time step.",
    )
    qsconv1: Optional[float] = Field(
        0.98,
        description="Fraction of grid points that must fulfill absolute wave height criteria EPSH1.",
    )
    qsconv2: Optional[float] = Field(
        0.98,
        description="Fraction of grid points that must fulfill relative wave height criteria EPSH2.",
    )
    qsconv3: Optional[float] = Field(
        0.98,
        description="Fraction of grid points that must fulfill sum. rel. wave action criteria EPSH3.",
    )
    qsconv4: Optional[float] = Field(
        0.98,
        description="Fraction of grid points that must fulfill rel. avg. wave steepness criteria EPSH4.",
    )
    qsconv5: Optional[float] = Field(
        0.98,
        description="Fraction of grid points that must fulfill avg. rel. wave period criteria EPSH5.",
    )
    lexpimp: Optional[bool] = Field(
        False,
        description="Use implicit schemes for frequencies lower than FREQEXP. Used only if ICOMP=0.",
    )
    freqexp: Optional[float] = Field(
        0.1,
        description="Minimum frequency for explicit schemes. Only used if LEXPIMP=True and ICOMP=0.",
    )
    epsh1: Optional[float] = Field(
        0.01, description="Convergence criteria for relative wave height."
    )
    epsh2: Optional[float] = Field(
        0.01, description="Convergence criteria for absolute wave height."
    )
    epsh3: Optional[float] = Field(
        0.01, description="Convergence criteria for the relative sum of wave action."
    )
    epsh4: Optional[float] = Field(
        0.01,
        description="Convergence criteria for the relative average wave steepness.",
    )
    epsh5: Optional[float] = Field(
        0.01, description="Convergence criteria for the relative average wave period."
    )
    lvector: Optional[bool] = Field(
        False,
        description="Use optimized propagation routines for large high performance computers. Try False first.",
    )
    ivector: Optional[int] = Field(
        2,
        description="Used if LVECTOR=True. Different flavors of communications and propagation styles.",
    )
    ladvtest: Optional[bool] = Field(
        False, description="For testing the advection schemes."
    )
    lchkconv: Optional[bool] = Field(
        False,
        description="Needs to be set to True for quasi-steady mode to compute and check the QSCONVi criteria.",
    )
    dtmin_dyn: Optional[float] = Field(
        1.0,
        description="Minimum time step (seconds) for dynamic integration. Controls the smallest time step for triads in SMETHOD.",
    )
    ndyniter: Optional[int] = Field(
        100,
        description="Maximum iterations for dynamic scheme before limiter is applied in the last step.",
    )
    dtmin_sin: Optional[float] = Field(
        1.0,
        description="Minimum time step for the full fractional step method, where each source term is integrated with its own fractional step.",
    )
    dtmin_snl4: Optional[float] = Field(
        1.0,
        description="Minimum time step for SNL4 source term in fractional step method.",
    )
    dtmin_sds: Optional[float] = Field(
        1.0,
        description="Minimum time step for SDS source term in fractional step method.",
    )
    dtmin_snl3: Optional[float] = Field(
        1.0,
        description="Minimum time step for SNL3 source term in fractional step method.",
    )
    dtmin_sbr: Optional[float] = Field(
        0.1,
        description="Minimum time step for SBR source term in fractional step method.",
    )
    dtmin_sbf: Optional[float] = Field(
        1.0,
        description="Minimum time step for SBF source term in fractional step method.",
    )
    ndyniter_sin: Optional[int] = Field(
        10,
        description="Maximum iterations for SIN source term in fractional step approach.",
    )
    ndyniter_snl4: Optional[int] = Field(
        10,
        description="Maximum iterations for SNL4 source term in fractional step approach.",
    )
    ndyniter_sds: Optional[int] = Field(
        10,
        description="Maximum iterations for SDS source term in fractional step approach.",
    )
    ndyniter_sbr: Optional[int] = Field(
        10,
        description="Maximum iterations for SBR source term in fractional step approach.",
    )
    ndyniter_snl3: Optional[int] = Field(
        10,
        description="Maximum iterations for SNL3 source term in fractional step approach.",
    )
    ndyniter_sbf: Optional[int] = Field(
        10,
        description="Maximum iterations for SBF source term in fractional step approach.",
    )
    lsoubound: Optional[bool] = Field(
        False,
        description="Do source terms on boundary. Useful for harbor studies and flume experiments.",
    )
    wae_solverthr: Optional[float] = Field(
        1e-06,
        description="Threshold for the Block-Jacobi or Block-Gauss-Seider solver.",
    )
    maxiter: Optional[int] = Field(
        1000, description="Maximum number of iterations for solver."
    )
    pmin: Optional[float] = Field(
        1.0, description="Maximum percentage of non-converged grid points allowed."
    )
    lnaninfchk: Optional[bool] = Field(
        False, description="Check for NaN and INF. Usually turned off for efficiency."
    )
    lzeta_setup: Optional[bool] = Field(
        False, description="Compute wave setup (simple momentum equation)."
    )
    zeta_meth: Optional[int] = Field(
        0, description="Method for wave setup calculation."
    )
    lsourceswam: Optional[bool] = Field(
        False, description="Use ECMWF WAM formulation for deep water physics."
    )
    block_gauss_seidel: Optional[bool] = Field(
        True,
        description="Use Gauss-Seidel method on each computer block. Faster and uses less memory, but iterations depend on number of processors.",
    )
    lnonl: Optional[bool] = Field(
        False,
        description="Solve the nonlinear system using simpler algorithm (Patankar).",
    )
    aspar_local_level: Optional[int] = Field(
        0,
        description="ASPAR locality level. Controls memory allocation and optimization strategies.",
    )
    l_solver_norm: Optional[bool] = Field(
        False,
        description="Compute solver norm ||A*x-b|| as termination check of Jacobi-Gauss-Seidel solver. Increases cost if True.",
    )
    laccel: Optional[bool] = Field(False, description="Enable acceleration for solver.")

    @field_validator("icomp")
    @classmethod
    def check_icomp(cls, v):
        if v not in [0, 1, 2, 3]:
            raise ValueError("ICOMP must be 0, 1, 2, or 3")
        return v

    @field_validator("amethod")
    @classmethod
    def check_amethod(cls, v):
        if v not in range(8):
            raise ValueError("AMETHOD must be between 0 and 7")
        return v

    @field_validator("smethod")
    @classmethod
    def check_smethod(cls, v):
        if v not in range(7):
            raise ValueError("SMETHOD must be between 0 and 6")
        return v

    @field_validator("dmethod")
    @classmethod
    def check_dmethod(cls, v):
        if v not in range(5):
            raise ValueError("DMETHOD must be between 0 and 4")
        return v

    @field_validator("rtheta")
    @classmethod
    def check_rtheta(cls, v):
        if not 0 <= v <= 1:
            raise ValueError("RTHETA must be between 0 and 1")
        return v

    @field_validator("maxcflth")
    @classmethod
    def check_maxcflth(cls, v):
        if v <= 0:
            raise ValueError("MAXCFLTH must be positive")
        return v

    @field_validator("fmethod")
    @classmethod
    def check_fmethod(cls, v):
        if v not in [0, 1]:
            raise ValueError("FMETHOD must be 0 or 1")
        return v

    @field_validator("maxcflsig")
    @classmethod
    def check_maxcflsig(cls, v):
        if v <= 0:
            raise ValueError("MAXCFLSIG must be positive")
        return v

    @field_validator("melim")
    @classmethod
    def check_melim(cls, v):
        if v not in [1, 2, 3]:
            raise ValueError("MELIM must be 1, 2, or 3")
        return v

    @field_validator("limfak")
    @classmethod
    def check_limfak(cls, v):
        if v <= 0:
            raise ValueError("LIMFAK must be positive")
        return v

    @field_validator("idiffr")
    @classmethod
    def check_idiffr(cls, v):
        if v not in [1, 2, 3]:
            raise ValueError("IDIFFR must be 1, 2, or 3")
        return v

    @field_validator("nqsiter")
    @classmethod
    def check_nqsiter(cls, v):
        if v < 1:
            raise ValueError("NQSITER must be at least 1")
        return v

    @field_validator("qsconv1")
    @classmethod
    def check_qsconv1(cls, v):
        if not 0 <= v <= 1:
            raise ValueError("QSCONV1 must be between 0 and 1")
        return v

    @field_validator("qsconv2")
    @classmethod
    def check_qsconv2(cls, v):
        if not 0 <= v <= 1:
            raise ValueError("QSCONV2 must be between 0 and 1")
        return v

    @field_validator("qsconv3")
    @classmethod
    def check_qsconv3(cls, v):
        if not 0 <= v <= 1:
            raise ValueError("QSCONV3 must be between 0 and 1")
        return v

    @field_validator("qsconv4")
    @classmethod
    def check_qsconv4(cls, v):
        if not 0 <= v <= 1:
            raise ValueError("QSCONV4 must be between 0 and 1")
        return v

    @field_validator("qsconv5")
    @classmethod
    def check_qsconv5(cls, v):
        if not 0 <= v <= 1:
            raise ValueError("QSCONV5 must be between 0 and 1")
        return v

    @field_validator("freqexp")
    @classmethod
    def check_freqexp(cls, v):
        if v <= 0:
            raise ValueError("FREQEXP must be positive")
        return v

    @field_validator("epsh1")
    @classmethod
    def check_epsh1(cls, v):
        if v <= 0:
            raise ValueError("EPSH1 must be positive")
        return v

    @field_validator("epsh2")
    @classmethod
    def check_epsh2(cls, v):
        if v <= 0:
            raise ValueError("EPSH2 must be positive")
        return v

    @field_validator("epsh3")
    @classmethod
    def check_epsh3(cls, v):
        if v <= 0:
            raise ValueError("EPSH3 must be positive")
        return v

    @field_validator("epsh4")
    @classmethod
    def check_epsh4(cls, v):
        if v <= 0:
            raise ValueError("EPSH4 must be positive")
        return v

    @field_validator("epsh5")
    @classmethod
    def check_epsh5(cls, v):
        if v <= 0:
            raise ValueError("EPSH5 must be positive")
        return v

    @field_validator("ivector")
    @classmethod
    def check_ivector(cls, v):
        if v not in range(1, 7):
            raise ValueError("IVECTOR must be between 1 and 6")
        return v

    @field_validator("dtmin_dyn")
    @classmethod
    def check_dtmin_dyn(cls, v):
        if v <= 0:
            raise ValueError("DTMIN_DYN must be positive")
        return v

    @field_validator("ndyniter")
    @classmethod
    def check_ndyniter(cls, v):
        if v < 1:
            raise ValueError("NDYNITER must be at least 1")
        return v

    @field_validator("dtmin_sin")
    @classmethod
    def check_dtmin_sin(cls, v):
        if v <= 0:
            raise ValueError("DTMIN_SIN must be positive")
        return v

    @field_validator("dtmin_snl4")
    @classmethod
    def check_dtmin_snl4(cls, v):
        if v <= 0:
            raise ValueError("DTMIN_SNL4 must be positive")
        return v

    @field_validator("dtmin_sds")
    @classmethod
    def check_dtmin_sds(cls, v):
        if v <= 0:
            raise ValueError("DTMIN_SDS must be positive")
        return v

    @field_validator("dtmin_snl3")
    @classmethod
    def check_dtmin_snl3(cls, v):
        if v <= 0:
            raise ValueError("DTMIN_SNL3 must be positive")
        return v

    @field_validator("dtmin_sbr")
    @classmethod
    def check_dtmin_sbr(cls, v):
        if v <= 0:
            raise ValueError("DTMIN_SBR must be positive")
        return v

    @field_validator("dtmin_sbf")
    @classmethod
    def check_dtmin_sbf(cls, v):
        if v <= 0:
            raise ValueError("DTMIN_SBF must be positive")
        return v

    @field_validator("ndyniter_sin")
    @classmethod
    def check_ndyniter_sin(cls, v):
        if v < 1:
            raise ValueError("NDYNITER_SIN must be at least 1")
        return v

    @field_validator("ndyniter_snl4")
    @classmethod
    def check_ndyniter_snl4(cls, v):
        if v < 1:
            raise ValueError("NDYNITER_SNL4 must be at least 1")
        return v

    @field_validator("ndyniter_sds")
    @classmethod
    def check_ndyniter_sds(cls, v):
        if v < 1:
            raise ValueError("NDYNITER_SDS must be at least 1")
        return v

    @field_validator("ndyniter_sbr")
    @classmethod
    def check_ndyniter_sbr(cls, v):
        if v < 1:
            raise ValueError("NDYNITER_SBR must be at least 1")
        return v

    @field_validator("ndyniter_snl3")
    @classmethod
    def check_ndyniter_snl3(cls, v):
        if v < 1:
            raise ValueError("NDYNITER_SNL3 must be at least 1")
        return v

    @field_validator("ndyniter_sbf")
    @classmethod
    def check_ndyniter_sbf(cls, v):
        if v < 1:
            raise ValueError("NDYNITER_SBF must be at least 1")
        return v

    @field_validator("wae_solverthr")
    @classmethod
    def check_wae_solverthr(cls, v):
        if v <= 0:
            raise ValueError("WAE_SOLVERTHR must be positive")
        return v

    @field_validator("maxiter")
    @classmethod
    def check_maxiter(cls, v):
        if v < 1:
            raise ValueError("MAXITER must be at least 1")
        return v

    @field_validator("pmin")
    @classmethod
    def check_pmin(cls, v):
        if not 0 <= v <= 100:
            raise ValueError("PMIN must be between 0 and 100")
        return v

    @field_validator("zeta_meth")
    @classmethod
    def check_zeta_meth(cls, v):
        if v < 0:
            raise ValueError("ZETA_METH must be non-negative")
        return v

    @field_validator("aspar_local_level")
    @classmethod
    def check_aspar_local_level(cls, v):
        if v not in range(6):
            raise ValueError("ASPAR_LOCAL_LEVEL must be between 0 and 5")
        return v

Attributes

icomp class-attribute instance-attribute

icomp: Optional[int] = Field(3, description='Controls the integration scheme for splitting and advection. 0: All explicit. 1: Implicit geographical advection. 2: Implicit advection and semi-implicit source terms. 3: Fully implicit, no splitting.')

amethod class-attribute instance-attribute

amethod: Optional[int] = Field(7, description='Controls the advection method in geographical space. Values 0-7 represent different schemes, including explicit, implicit, and PETSc-based methods.')

smethod class-attribute instance-attribute

smethod: Optional[int] = Field(6, description='Controls the integration method for source terms. 0: No source terms. 1-6: Various splitting and integration schemes.')

dmethod class-attribute instance-attribute

dmethod: Optional[int] = Field(2, description='Controls the numerical method in directional space. 0: No advection. 1-4: Various schemes including Crank-Nicholson, Ultimate Quickest, RK5-WENO, and Explicit FVM Upwind.')

rtheta class-attribute instance-attribute

rtheta: Optional[float] = Field(0.5, description='Weighing factor for DMETHOD = 1. Only useful for Crank Nicholson integration with CFL <= 2.')

litersplit class-attribute instance-attribute

litersplit: Optional[bool] = Field(False, description='Splitting method. True: double Strang split. False: simple split (more efficient).')

lfilterth class-attribute instance-attribute

lfilterth: Optional[bool] = Field(False, description='Use a CFL filter to limit the advection velocity in directional space. Similar to WW3, but mostly unused as WWMII is always stable.')

maxcflth class-attribute instance-attribute

maxcflth: Optional[float] = Field(1.0, description='Maximum CFL number in Theta space. Used only if LFILTERTH=True.')

fmethod class-attribute instance-attribute

fmethod: Optional[int] = Field(1, description='Controls the numerical method in frequency space. 0: No advection. 1: Ultimate Quickest as in WW3 (best).')

lfiltersig class-attribute instance-attribute

lfiltersig: Optional[bool] = Field(False, description='Limit the advection velocity in frequency space. Usually False.')

maxcflsig class-attribute instance-attribute

maxcflsig: Optional[float] = Field(1.0, description='Maximum CFL number in frequency space. Used only if LFILTERSIG=True.')

llimt class-attribute instance-attribute

llimt: Optional[bool] = Field(True, description='Switch on/off Action limiter. Must mostly be turned on.')

melim class-attribute instance-attribute

melim: Optional[int] = Field(1, description='Formulation for the action limiter. 1: WAM group (1988). 2: Hersbach Janssen (1999). 3: For Cycle 4 formulation.')

limfak class-attribute instance-attribute

limfak: Optional[float] = Field(0.1, description='Proportionality coefficient for the action limiter. MAX_DAC_DT = Limfak * Limiter.')

ldifr class-attribute instance-attribute

ldifr: Optional[bool] = Field(False, description='Use phase decoupled diffraction approximation. Usually True; if crash, use False.')

idiffr class-attribute instance-attribute

idiffr: Optional[int] = Field(1, description='Extended WAE accounting for higher order effects. 1: Holthuijsen et al. 2: Liau et al. 3: Toledo et al.')

lconv class-attribute instance-attribute

lconv: Optional[bool] = Field(False, description='Estimate convergence criteria and write to disk (quasi-steady - qstea.out).')

lcfl class-attribute instance-attribute

lcfl: Optional[bool] = Field(False, description='Write out CFL numbers. Use False to save time.')

nqsiter class-attribute instance-attribute

nqsiter: Optional[int] = Field(1, description='Number of quasi-steady (Q-S) sub-divisions within each WWM time step.')

qsconv1 class-attribute instance-attribute

qsconv1: Optional[float] = Field(0.98, description='Fraction of grid points that must fulfill absolute wave height criteria EPSH1.')

qsconv2 class-attribute instance-attribute

qsconv2: Optional[float] = Field(0.98, description='Fraction of grid points that must fulfill relative wave height criteria EPSH2.')

qsconv3 class-attribute instance-attribute

qsconv3: Optional[float] = Field(0.98, description='Fraction of grid points that must fulfill sum. rel. wave action criteria EPSH3.')

qsconv4 class-attribute instance-attribute

qsconv4: Optional[float] = Field(0.98, description='Fraction of grid points that must fulfill rel. avg. wave steepness criteria EPSH4.')

qsconv5 class-attribute instance-attribute

qsconv5: Optional[float] = Field(0.98, description='Fraction of grid points that must fulfill avg. rel. wave period criteria EPSH5.')

lexpimp class-attribute instance-attribute

lexpimp: Optional[bool] = Field(False, description='Use implicit schemes for frequencies lower than FREQEXP. Used only if ICOMP=0.')

freqexp class-attribute instance-attribute

freqexp: Optional[float] = Field(0.1, description='Minimum frequency for explicit schemes. Only used if LEXPIMP=True and ICOMP=0.')

epsh1 class-attribute instance-attribute

epsh1: Optional[float] = Field(0.01, description='Convergence criteria for relative wave height.')

epsh2 class-attribute instance-attribute

epsh2: Optional[float] = Field(0.01, description='Convergence criteria for absolute wave height.')

epsh3 class-attribute instance-attribute

epsh3: Optional[float] = Field(0.01, description='Convergence criteria for the relative sum of wave action.')

epsh4 class-attribute instance-attribute

epsh4: Optional[float] = Field(0.01, description='Convergence criteria for the relative average wave steepness.')

epsh5 class-attribute instance-attribute

epsh5: Optional[float] = Field(0.01, description='Convergence criteria for the relative average wave period.')

lvector class-attribute instance-attribute

lvector: Optional[bool] = Field(False, description='Use optimized propagation routines for large high performance computers. Try False first.')

ivector class-attribute instance-attribute

ivector: Optional[int] = Field(2, description='Used if LVECTOR=True. Different flavors of communications and propagation styles.')

ladvtest class-attribute instance-attribute

ladvtest: Optional[bool] = Field(False, description='For testing the advection schemes.')

lchkconv class-attribute instance-attribute

lchkconv: Optional[bool] = Field(False, description='Needs to be set to True for quasi-steady mode to compute and check the QSCONVi criteria.')

dtmin_dyn class-attribute instance-attribute

dtmin_dyn: Optional[float] = Field(1.0, description='Minimum time step (seconds) for dynamic integration. Controls the smallest time step for triads in SMETHOD.')

ndyniter class-attribute instance-attribute

ndyniter: Optional[int] = Field(100, description='Maximum iterations for dynamic scheme before limiter is applied in the last step.')

dtmin_sin class-attribute instance-attribute

dtmin_sin: Optional[float] = Field(1.0, description='Minimum time step for the full fractional step method, where each source term is integrated with its own fractional step.')

dtmin_snl4 class-attribute instance-attribute

dtmin_snl4: Optional[float] = Field(1.0, description='Minimum time step for SNL4 source term in fractional step method.')

dtmin_sds class-attribute instance-attribute

dtmin_sds: Optional[float] = Field(1.0, description='Minimum time step for SDS source term in fractional step method.')

dtmin_snl3 class-attribute instance-attribute

dtmin_snl3: Optional[float] = Field(1.0, description='Minimum time step for SNL3 source term in fractional step method.')

dtmin_sbr class-attribute instance-attribute

dtmin_sbr: Optional[float] = Field(0.1, description='Minimum time step for SBR source term in fractional step method.')

dtmin_sbf class-attribute instance-attribute

dtmin_sbf: Optional[float] = Field(1.0, description='Minimum time step for SBF source term in fractional step method.')

ndyniter_sin class-attribute instance-attribute

ndyniter_sin: Optional[int] = Field(10, description='Maximum iterations for SIN source term in fractional step approach.')

ndyniter_snl4 class-attribute instance-attribute

ndyniter_snl4: Optional[int] = Field(10, description='Maximum iterations for SNL4 source term in fractional step approach.')

ndyniter_sds class-attribute instance-attribute

ndyniter_sds: Optional[int] = Field(10, description='Maximum iterations for SDS source term in fractional step approach.')

ndyniter_sbr class-attribute instance-attribute

ndyniter_sbr: Optional[int] = Field(10, description='Maximum iterations for SBR source term in fractional step approach.')

ndyniter_snl3 class-attribute instance-attribute

ndyniter_snl3: Optional[int] = Field(10, description='Maximum iterations for SNL3 source term in fractional step approach.')

ndyniter_sbf class-attribute instance-attribute

ndyniter_sbf: Optional[int] = Field(10, description='Maximum iterations for SBF source term in fractional step approach.')

lsoubound class-attribute instance-attribute

lsoubound: Optional[bool] = Field(False, description='Do source terms on boundary. Useful for harbor studies and flume experiments.')

wae_solverthr class-attribute instance-attribute

wae_solverthr: Optional[float] = Field(1e-06, description='Threshold for the Block-Jacobi or Block-Gauss-Seider solver.')

maxiter class-attribute instance-attribute

maxiter: Optional[int] = Field(1000, description='Maximum number of iterations for solver.')

pmin class-attribute instance-attribute

pmin: Optional[float] = Field(1.0, description='Maximum percentage of non-converged grid points allowed.')

lnaninfchk class-attribute instance-attribute

lnaninfchk: Optional[bool] = Field(False, description='Check for NaN and INF. Usually turned off for efficiency.')

lzeta_setup class-attribute instance-attribute

lzeta_setup: Optional[bool] = Field(False, description='Compute wave setup (simple momentum equation).')

zeta_meth class-attribute instance-attribute

zeta_meth: Optional[int] = Field(0, description='Method for wave setup calculation.')

lsourceswam class-attribute instance-attribute

lsourceswam: Optional[bool] = Field(False, description='Use ECMWF WAM formulation for deep water physics.')

block_gauss_seidel class-attribute instance-attribute

block_gauss_seidel: Optional[bool] = Field(True, description='Use Gauss-Seidel method on each computer block. Faster and uses less memory, but iterations depend on number of processors.')

lnonl class-attribute instance-attribute

lnonl: Optional[bool] = Field(False, description='Solve the nonlinear system using simpler algorithm (Patankar).')

aspar_local_level class-attribute instance-attribute

aspar_local_level: Optional[int] = Field(0, description='ASPAR locality level. Controls memory allocation and optimization strategies.')

l_solver_norm class-attribute instance-attribute

l_solver_norm: Optional[bool] = Field(False, description='Compute solver norm ||A*x-b|| as termination check of Jacobi-Gauss-Seidel solver. Increases cost if True.')

laccel class-attribute instance-attribute

laccel: Optional[bool] = Field(False, description='Enable acceleration for solver.')

Functions

check_icomp classmethod

check_icomp(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("icomp")
@classmethod
def check_icomp(cls, v):
    if v not in [0, 1, 2, 3]:
        raise ValueError("ICOMP must be 0, 1, 2, or 3")
    return v

check_amethod classmethod

check_amethod(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("amethod")
@classmethod
def check_amethod(cls, v):
    if v not in range(8):
        raise ValueError("AMETHOD must be between 0 and 7")
    return v

check_smethod classmethod

check_smethod(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("smethod")
@classmethod
def check_smethod(cls, v):
    if v not in range(7):
        raise ValueError("SMETHOD must be between 0 and 6")
    return v

check_dmethod classmethod

check_dmethod(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("dmethod")
@classmethod
def check_dmethod(cls, v):
    if v not in range(5):
        raise ValueError("DMETHOD must be between 0 and 4")
    return v

check_rtheta classmethod

check_rtheta(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("rtheta")
@classmethod
def check_rtheta(cls, v):
    if not 0 <= v <= 1:
        raise ValueError("RTHETA must be between 0 and 1")
    return v

check_maxcflth classmethod

check_maxcflth(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("maxcflth")
@classmethod
def check_maxcflth(cls, v):
    if v <= 0:
        raise ValueError("MAXCFLTH must be positive")
    return v

check_fmethod classmethod

check_fmethod(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("fmethod")
@classmethod
def check_fmethod(cls, v):
    if v not in [0, 1]:
        raise ValueError("FMETHOD must be 0 or 1")
    return v

check_maxcflsig classmethod

check_maxcflsig(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("maxcflsig")
@classmethod
def check_maxcflsig(cls, v):
    if v <= 0:
        raise ValueError("MAXCFLSIG must be positive")
    return v

check_melim classmethod

check_melim(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("melim")
@classmethod
def check_melim(cls, v):
    if v not in [1, 2, 3]:
        raise ValueError("MELIM must be 1, 2, or 3")
    return v

check_limfak classmethod

check_limfak(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("limfak")
@classmethod
def check_limfak(cls, v):
    if v <= 0:
        raise ValueError("LIMFAK must be positive")
    return v

check_idiffr classmethod

check_idiffr(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("idiffr")
@classmethod
def check_idiffr(cls, v):
    if v not in [1, 2, 3]:
        raise ValueError("IDIFFR must be 1, 2, or 3")
    return v

check_nqsiter classmethod

check_nqsiter(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("nqsiter")
@classmethod
def check_nqsiter(cls, v):
    if v < 1:
        raise ValueError("NQSITER must be at least 1")
    return v

check_qsconv1 classmethod

check_qsconv1(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("qsconv1")
@classmethod
def check_qsconv1(cls, v):
    if not 0 <= v <= 1:
        raise ValueError("QSCONV1 must be between 0 and 1")
    return v

check_qsconv2 classmethod

check_qsconv2(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("qsconv2")
@classmethod
def check_qsconv2(cls, v):
    if not 0 <= v <= 1:
        raise ValueError("QSCONV2 must be between 0 and 1")
    return v

check_qsconv3 classmethod

check_qsconv3(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("qsconv3")
@classmethod
def check_qsconv3(cls, v):
    if not 0 <= v <= 1:
        raise ValueError("QSCONV3 must be between 0 and 1")
    return v

check_qsconv4 classmethod

check_qsconv4(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("qsconv4")
@classmethod
def check_qsconv4(cls, v):
    if not 0 <= v <= 1:
        raise ValueError("QSCONV4 must be between 0 and 1")
    return v

check_qsconv5 classmethod

check_qsconv5(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("qsconv5")
@classmethod
def check_qsconv5(cls, v):
    if not 0 <= v <= 1:
        raise ValueError("QSCONV5 must be between 0 and 1")
    return v

check_freqexp classmethod

check_freqexp(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("freqexp")
@classmethod
def check_freqexp(cls, v):
    if v <= 0:
        raise ValueError("FREQEXP must be positive")
    return v

check_epsh1 classmethod

check_epsh1(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("epsh1")
@classmethod
def check_epsh1(cls, v):
    if v <= 0:
        raise ValueError("EPSH1 must be positive")
    return v

check_epsh2 classmethod

check_epsh2(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("epsh2")
@classmethod
def check_epsh2(cls, v):
    if v <= 0:
        raise ValueError("EPSH2 must be positive")
    return v

check_epsh3 classmethod

check_epsh3(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("epsh3")
@classmethod
def check_epsh3(cls, v):
    if v <= 0:
        raise ValueError("EPSH3 must be positive")
    return v

check_epsh4 classmethod

check_epsh4(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("epsh4")
@classmethod
def check_epsh4(cls, v):
    if v <= 0:
        raise ValueError("EPSH4 must be positive")
    return v

check_epsh5 classmethod

check_epsh5(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("epsh5")
@classmethod
def check_epsh5(cls, v):
    if v <= 0:
        raise ValueError("EPSH5 must be positive")
    return v

check_ivector classmethod

check_ivector(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("ivector")
@classmethod
def check_ivector(cls, v):
    if v not in range(1, 7):
        raise ValueError("IVECTOR must be between 1 and 6")
    return v

check_dtmin_dyn classmethod

check_dtmin_dyn(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("dtmin_dyn")
@classmethod
def check_dtmin_dyn(cls, v):
    if v <= 0:
        raise ValueError("DTMIN_DYN must be positive")
    return v

check_ndyniter classmethod

check_ndyniter(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("ndyniter")
@classmethod
def check_ndyniter(cls, v):
    if v < 1:
        raise ValueError("NDYNITER must be at least 1")
    return v

check_dtmin_sin classmethod

check_dtmin_sin(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("dtmin_sin")
@classmethod
def check_dtmin_sin(cls, v):
    if v <= 0:
        raise ValueError("DTMIN_SIN must be positive")
    return v

check_dtmin_snl4 classmethod

check_dtmin_snl4(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("dtmin_snl4")
@classmethod
def check_dtmin_snl4(cls, v):
    if v <= 0:
        raise ValueError("DTMIN_SNL4 must be positive")
    return v

check_dtmin_sds classmethod

check_dtmin_sds(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("dtmin_sds")
@classmethod
def check_dtmin_sds(cls, v):
    if v <= 0:
        raise ValueError("DTMIN_SDS must be positive")
    return v

check_dtmin_snl3 classmethod

check_dtmin_snl3(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("dtmin_snl3")
@classmethod
def check_dtmin_snl3(cls, v):
    if v <= 0:
        raise ValueError("DTMIN_SNL3 must be positive")
    return v

check_dtmin_sbr classmethod

check_dtmin_sbr(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("dtmin_sbr")
@classmethod
def check_dtmin_sbr(cls, v):
    if v <= 0:
        raise ValueError("DTMIN_SBR must be positive")
    return v

check_dtmin_sbf classmethod

check_dtmin_sbf(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("dtmin_sbf")
@classmethod
def check_dtmin_sbf(cls, v):
    if v <= 0:
        raise ValueError("DTMIN_SBF must be positive")
    return v

check_ndyniter_sin classmethod

check_ndyniter_sin(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("ndyniter_sin")
@classmethod
def check_ndyniter_sin(cls, v):
    if v < 1:
        raise ValueError("NDYNITER_SIN must be at least 1")
    return v

check_ndyniter_snl4 classmethod

check_ndyniter_snl4(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("ndyniter_snl4")
@classmethod
def check_ndyniter_snl4(cls, v):
    if v < 1:
        raise ValueError("NDYNITER_SNL4 must be at least 1")
    return v

check_ndyniter_sds classmethod

check_ndyniter_sds(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("ndyniter_sds")
@classmethod
def check_ndyniter_sds(cls, v):
    if v < 1:
        raise ValueError("NDYNITER_SDS must be at least 1")
    return v

check_ndyniter_sbr classmethod

check_ndyniter_sbr(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("ndyniter_sbr")
@classmethod
def check_ndyniter_sbr(cls, v):
    if v < 1:
        raise ValueError("NDYNITER_SBR must be at least 1")
    return v

check_ndyniter_snl3 classmethod

check_ndyniter_snl3(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("ndyniter_snl3")
@classmethod
def check_ndyniter_snl3(cls, v):
    if v < 1:
        raise ValueError("NDYNITER_SNL3 must be at least 1")
    return v

check_ndyniter_sbf classmethod

check_ndyniter_sbf(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("ndyniter_sbf")
@classmethod
def check_ndyniter_sbf(cls, v):
    if v < 1:
        raise ValueError("NDYNITER_SBF must be at least 1")
    return v

check_wae_solverthr classmethod

check_wae_solverthr(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("wae_solverthr")
@classmethod
def check_wae_solverthr(cls, v):
    if v <= 0:
        raise ValueError("WAE_SOLVERTHR must be positive")
    return v

check_maxiter classmethod

check_maxiter(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("maxiter")
@classmethod
def check_maxiter(cls, v):
    if v < 1:
        raise ValueError("MAXITER must be at least 1")
    return v

check_pmin classmethod

check_pmin(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("pmin")
@classmethod
def check_pmin(cls, v):
    if not 0 <= v <= 100:
        raise ValueError("PMIN must be between 0 and 100")
    return v

check_zeta_meth classmethod

check_zeta_meth(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("zeta_meth")
@classmethod
def check_zeta_meth(cls, v):
    if v < 0:
        raise ValueError("ZETA_METH must be non-negative")
    return v

check_aspar_local_level classmethod

check_aspar_local_level(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("aspar_local_level")
@classmethod
def check_aspar_local_level(cls, v):
    if v not in range(6):
        raise ValueError("ASPAR_LOCAL_LEVEL must be between 0 and 5")
    return v

Petscoptions

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/wwminput.py
class Petscoptions(NamelistBaseModel):
    ksptype: Optional[str] = Field(
        "LGMRES",
        description="Controls the linear solver type used by PETSc. Options include GMRES, LGMRES (augmented GMRES), DGMRES (deflated GMRES), PGMRES (pipelined GMRES), and KSPBCGSL (variant of Enhanced BiCGStab(L)).",
    )
    rtol: Optional[float] = Field(
        1e-20,
        description="Relative convergence tolerance, representing the relative decrease in the residual norm for the iterative solver.",
    )
    abstol: Optional[float] = Field(
        1e-20,
        description="Absolute convergence tolerance, representing the absolute size of the residual norm for the iterative solver.",
    )
    dtol: Optional[float] = Field(
        10000.0, description="Divergence tolerance for the iterative solver."
    )
    maxits: Optional[int] = Field(
        1000,
        description="Maximum number of iterations allowed for the iterative solver.",
    )
    initialguessnonzero: Optional[bool] = Field(
        False,
        description="Boolean flag indicating whether the initial guess for the iterative solver is nonzero.",
    )
    gmrespreallocate: Optional[bool] = Field(
        True,
        description="Boolean flag indicating whether GMRES and FGMRES should preallocate all needed work vectors at initial setup.",
    )
    pctype: Optional[str] = Field(
        "SOR",
        description="Controls the preconditioner type used by PETSc. Options include SOR (successive over relaxation), ASM (additive Schwarz method), HYPRE (LLNL package), SPAI (Sparse Approximate Inverse), and NONE (no preconditioning).",
    )

    @field_validator("ksptype")
    @classmethod
    def validate_ksptype(cls, v):
        valid_types = ["GMRES", "LGMRES", "DGMRES", "PGMRES", "KSPBCGSL"]
        if v.upper() not in valid_types:
            raise ValueError(f"KSPTYPE must be one of {valid_types}")
        return v.upper()

    @field_validator("rtol")
    @classmethod
    def validate_rtol(cls, v):
        if v <= 0:
            raise ValueError("RTOL must be positive")
        return v

    @field_validator("abstol")
    @classmethod
    def validate_abstol(cls, v):
        if v <= 0:
            raise ValueError("ABSTOL must be positive")
        return v

    @field_validator("dtol")
    @classmethod
    def validate_dtol(cls, v):
        if v <= 0:
            raise ValueError("DTOL must be positive")
        return v

    @field_validator("maxits")
    @classmethod
    def validate_maxits(cls, v):
        if v <= 0 or not isinstance(v, int):
            raise ValueError("MAXITS must be a positive integer")
        return v

    @field_validator("initialguessnonzero")
    @classmethod
    def validate_initialguessnonzero(cls, v):
        return v

    @field_validator("gmrespreallocate")
    @classmethod
    def validate_gmrespreallocate(cls, v):
        return v

    @field_validator("pctype")
    @classmethod
    def validate_pctype(cls, v):
        valid_types = ["SOR", "ASM", "HYPRE", "SPAI", "NONE"]
        if v.upper() not in valid_types:
            raise ValueError(f"PCTYPE must be one of {valid_types}")
        return v.upper()

Attributes

ksptype class-attribute instance-attribute

ksptype: Optional[str] = Field('LGMRES', description='Controls the linear solver type used by PETSc. Options include GMRES, LGMRES (augmented GMRES), DGMRES (deflated GMRES), PGMRES (pipelined GMRES), and KSPBCGSL (variant of Enhanced BiCGStab(L)).')

rtol class-attribute instance-attribute

rtol: Optional[float] = Field(1e-20, description='Relative convergence tolerance, representing the relative decrease in the residual norm for the iterative solver.')

abstol class-attribute instance-attribute

abstol: Optional[float] = Field(1e-20, description='Absolute convergence tolerance, representing the absolute size of the residual norm for the iterative solver.')

dtol class-attribute instance-attribute

dtol: Optional[float] = Field(10000.0, description='Divergence tolerance for the iterative solver.')

maxits class-attribute instance-attribute

maxits: Optional[int] = Field(1000, description='Maximum number of iterations allowed for the iterative solver.')

initialguessnonzero class-attribute instance-attribute

initialguessnonzero: Optional[bool] = Field(False, description='Boolean flag indicating whether the initial guess for the iterative solver is nonzero.')

gmrespreallocate class-attribute instance-attribute

gmrespreallocate: Optional[bool] = Field(True, description='Boolean flag indicating whether GMRES and FGMRES should preallocate all needed work vectors at initial setup.')

pctype class-attribute instance-attribute

pctype: Optional[str] = Field('SOR', description='Controls the preconditioner type used by PETSc. Options include SOR (successive over relaxation), ASM (additive Schwarz method), HYPRE (LLNL package), SPAI (Sparse Approximate Inverse), and NONE (no preconditioning).')

Functions

validate_ksptype classmethod

validate_ksptype(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("ksptype")
@classmethod
def validate_ksptype(cls, v):
    valid_types = ["GMRES", "LGMRES", "DGMRES", "PGMRES", "KSPBCGSL"]
    if v.upper() not in valid_types:
        raise ValueError(f"KSPTYPE must be one of {valid_types}")
    return v.upper()

validate_rtol classmethod

validate_rtol(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("rtol")
@classmethod
def validate_rtol(cls, v):
    if v <= 0:
        raise ValueError("RTOL must be positive")
    return v

validate_abstol classmethod

validate_abstol(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("abstol")
@classmethod
def validate_abstol(cls, v):
    if v <= 0:
        raise ValueError("ABSTOL must be positive")
    return v

validate_dtol classmethod

validate_dtol(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("dtol")
@classmethod
def validate_dtol(cls, v):
    if v <= 0:
        raise ValueError("DTOL must be positive")
    return v

validate_maxits classmethod

validate_maxits(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("maxits")
@classmethod
def validate_maxits(cls, v):
    if v <= 0 or not isinstance(v, int):
        raise ValueError("MAXITS must be a positive integer")
    return v

validate_initialguessnonzero classmethod

validate_initialguessnonzero(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("initialguessnonzero")
@classmethod
def validate_initialguessnonzero(cls, v):
    return v

validate_gmrespreallocate classmethod

validate_gmrespreallocate(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("gmrespreallocate")
@classmethod
def validate_gmrespreallocate(cls, v):
    return v

validate_pctype classmethod

validate_pctype(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("pctype")
@classmethod
def validate_pctype(cls, v):
    valid_types = ["SOR", "ASM", "HYPRE", "SPAI", "NONE"]
    if v.upper() not in valid_types:
        raise ValueError(f"PCTYPE must be one of {valid_types}")
    return v.upper()

Proc

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/wwminput.py
class Proc(NamelistBaseModel):
    procname: Optional[str] = Field(
        "ACS", description="Project name for the simulation"
    )
    dimmode: Optional[int] = Field(
        2, description="Mode of run: 2 for 2D (always 2D when coupled to SCHISM)"
    )
    lstea: Optional[bool] = Field(
        False, description="Steady mode flag (under development)"
    )
    lqstea: Optional[bool] = Field(
        False,
        description="Quasi-Steady Mode flag. If True, WWM-II performs subiterations defined as DELTC/NQSITER unless QSCONVI is reached",
    )
    lsphe: Optional[bool] = Field(
        True, description="Flag for spherical coordinates (lon/lat)"
    )
    lnautin: Optional[bool] = Field(
        True,
        description="Flag for nautical convention in input angles (degrees). Recommended to be True",
    )
    lnautout: Optional[bool] = Field(
        True,
        description="Flag for nautical convention in output angles. If True, 0 is from north, 90 is from east. If False, mathematical convention is used (0: to east, 90: to north)",
    )
    lmono_in: Optional[bool] = Field(
        False,
        description="Flag for prescribing monochromatic wave height Hmono as boundary conditions. Incident wave is defined as Hmono = sqrt(2) * Hs",
    )
    lmono_out: Optional[bool] = Field(
        False, description="Flag for outputting wave heights in terms of Lmono"
    )
    begtc: Optional[str] = Field(
        "20200101.000000",
        description="Start time of the simulation in format 'yyyymmdd.hhmmss'",
    )
    deltc: Optional[int] = Field(
        600, description="Time step in seconds (must match dt*nstep_wwm in SCHISM)"
    )
    unitc: Optional[str] = Field(
        "SEC", description="Unit of time step (SEC for seconds)"
    )
    endtc: Optional[str] = Field(
        "20200201.000000",
        description="End time of the simulation in format 'yyyymmdd.hhmmss'",
    )
    dmin: Optional[float] = Field(
        0.01, description="Minimum water depth. Must be the same as h0 in SCHISM"
    )

    @field_validator("procname")
    def validate_procname(cls, v):
        if not v.strip():
            raise ValueError("Project name cannot be empty")
        return v

    @field_validator("dimmode")
    def validate_dimmode(cls, v):
        if v != 2:
            raise ValueError("DIMMODE must be 2 when coupled to SCHISM")
        return v

    @field_validator("lstea")
    def validate_lstea(cls, v):
        return v

    @field_validator("lqstea")
    def validate_lqstea(cls, v):
        return v

    @field_validator("lsphe")
    def validate_lsphe(cls, v):
        return v

    @field_validator("lnautin")
    def validate_lnautin(cls, v):
        return v

    @field_validator("lnautout")
    def validate_lnautout(cls, v):
        return v

    @field_validator("lmono_in")
    def validate_lmono_in(cls, v):
        return v

    @field_validator("lmono_out")
    def validate_lmono_out(cls, v):
        return v

    @field_validator("begtc")
    def validate_begtc(cls, v):
        try:
            datetime.strptime(v, "%Y%m%d.%H%M%S")
        except ValueError:
            raise ValueError("Invalid date format for BEGTC")
        return v

    @field_validator("deltc")
    def validate_deltc(cls, v):
        if v <= 0:
            raise ValueError("DELTC must be positive")
        return v

    @field_validator("unitc")
    def validate_unitc(cls, v):
        if v.upper() != "SEC":
            raise ValueError("UNITC must be SEC")
        return v.upper()

    @field_validator("endtc")
    def validate_endtc(cls, v):
        try:
            datetime.strptime(v, "%Y%m%d.%H%M%S")
        except ValueError:
            raise ValueError("Invalid date format for ENDTC")
        return v

    @field_validator("dmin")
    def validate_dmin(cls, v):
        if v <= 0:
            raise ValueError("DMIN must be positive")
        return v

Attributes

procname class-attribute instance-attribute

procname: Optional[str] = Field('ACS', description='Project name for the simulation')

dimmode class-attribute instance-attribute

dimmode: Optional[int] = Field(2, description='Mode of run: 2 for 2D (always 2D when coupled to SCHISM)')

lstea class-attribute instance-attribute

lstea: Optional[bool] = Field(False, description='Steady mode flag (under development)')

lqstea class-attribute instance-attribute

lqstea: Optional[bool] = Field(False, description='Quasi-Steady Mode flag. If True, WWM-II performs subiterations defined as DELTC/NQSITER unless QSCONVI is reached')

lsphe class-attribute instance-attribute

lsphe: Optional[bool] = Field(True, description='Flag for spherical coordinates (lon/lat)')

lnautin class-attribute instance-attribute

lnautin: Optional[bool] = Field(True, description='Flag for nautical convention in input angles (degrees). Recommended to be True')

lnautout class-attribute instance-attribute

lnautout: Optional[bool] = Field(True, description='Flag for nautical convention in output angles. If True, 0 is from north, 90 is from east. If False, mathematical convention is used (0: to east, 90: to north)')

lmono_in class-attribute instance-attribute

lmono_in: Optional[bool] = Field(False, description='Flag for prescribing monochromatic wave height Hmono as boundary conditions. Incident wave is defined as Hmono = sqrt(2) * Hs')

lmono_out class-attribute instance-attribute

lmono_out: Optional[bool] = Field(False, description='Flag for outputting wave heights in terms of Lmono')

begtc class-attribute instance-attribute

begtc: Optional[str] = Field('20200101.000000', description="Start time of the simulation in format 'yyyymmdd.hhmmss'")

deltc class-attribute instance-attribute

deltc: Optional[int] = Field(600, description='Time step in seconds (must match dt*nstep_wwm in SCHISM)')

unitc class-attribute instance-attribute

unitc: Optional[str] = Field('SEC', description='Unit of time step (SEC for seconds)')

endtc class-attribute instance-attribute

endtc: Optional[str] = Field('20200201.000000', description="End time of the simulation in format 'yyyymmdd.hhmmss'")

dmin class-attribute instance-attribute

dmin: Optional[float] = Field(0.01, description='Minimum water depth. Must be the same as h0 in SCHISM')

Functions

validate_procname

validate_procname(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("procname")
def validate_procname(cls, v):
    if not v.strip():
        raise ValueError("Project name cannot be empty")
    return v

validate_dimmode

validate_dimmode(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("dimmode")
def validate_dimmode(cls, v):
    if v != 2:
        raise ValueError("DIMMODE must be 2 when coupled to SCHISM")
    return v

validate_lstea

validate_lstea(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("lstea")
def validate_lstea(cls, v):
    return v

validate_lqstea

validate_lqstea(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("lqstea")
def validate_lqstea(cls, v):
    return v

validate_lsphe

validate_lsphe(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("lsphe")
def validate_lsphe(cls, v):
    return v

validate_lnautin

validate_lnautin(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("lnautin")
def validate_lnautin(cls, v):
    return v

validate_lnautout

validate_lnautout(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("lnautout")
def validate_lnautout(cls, v):
    return v

validate_lmono_in

validate_lmono_in(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("lmono_in")
def validate_lmono_in(cls, v):
    return v

validate_lmono_out

validate_lmono_out(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("lmono_out")
def validate_lmono_out(cls, v):
    return v

validate_begtc

validate_begtc(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("begtc")
def validate_begtc(cls, v):
    try:
        datetime.strptime(v, "%Y%m%d.%H%M%S")
    except ValueError:
        raise ValueError("Invalid date format for BEGTC")
    return v

validate_deltc

validate_deltc(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("deltc")
def validate_deltc(cls, v):
    if v <= 0:
        raise ValueError("DELTC must be positive")
    return v

validate_unitc

validate_unitc(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("unitc")
def validate_unitc(cls, v):
    if v.upper() != "SEC":
        raise ValueError("UNITC must be SEC")
    return v.upper()

validate_endtc

validate_endtc(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("endtc")
def validate_endtc(cls, v):
    try:
        datetime.strptime(v, "%Y%m%d.%H%M%S")
    except ValueError:
        raise ValueError("Invalid date format for ENDTC")
    return v

validate_dmin

validate_dmin(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("dmin")
def validate_dmin(cls, v):
    if v <= 0:
        raise ValueError("DMIN must be positive")
    return v

Station

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/wwminput.py
class Station(NamelistBaseModel):
    begtc: Optional[str] = Field(
        "20200101.000000",
        description="Start simulation time in 'yyyymmdd.hhmmss' format. Must fit the simulation time, otherwise no output is generated. Defaults to PROC%BEGTC if not specified.",
    )
    deltc: Optional[int] = Field(
        3600,
        description="Time step for output in seconds. If smaller than simulation time step, the latter is used. Used for better 1D and 2D spectra analysis.",
    )
    unitc: Optional[str] = Field(
        "SEC", description="Time unit for DELTC. Only 'SEC' is currently supported."
    )
    endtc: Optional[str] = Field(
        "20200201.000000",
        description="Stop time for simulation in 'yyyymmdd.hhmmss' format. Defaults to PROC%ENDC if not specified.",
    )
    definetc: Optional[int] = Field(
        -1,
        description="Time for definition of station files in seconds. If negative or unset, only one file is generated. Otherwise, it defines the interval for creating output files (e.g., 86400 for daily files).",
    )
    outstyle: Optional[str] = Field(
        "NC",
        description="Output option. 'NO' for no output, 'STE' for classic station output (default), 'NC' for netCDF output.",
    )
    multipleout: Optional[int] = Field(
        0,
        description="Output file configuration. 0 for a single netCDF file using MPI_reduce (default), 1 for separate netCDF files for each process.",
    )
    use_single_out: Optional[bool] = Field(
        True,
        description="Use single precision in the output of model variables. True by default.",
    )
    paramwrite: Optional[bool] = Field(
        True,
        description="Write the physical parametrization and chosen numerical method in the netCDF file. True by default.",
    )
    fileout: Optional[str] = Field(
        "wwm_sta.nc", description="Output file name (not used)."
    )
    loutiter: Optional[bool] = Field(
        False, description="Boolean flag for output iteration."
    )
    iouts: Optional[int] = Field(12, description="Number of output stations.")
    nouts: Optional[list[str]] = Field(
        [
            "BatemansBay",
            "Brisbane",
            "ByronBay",
            "Cairns",
            "CoffsHbr",
            "CrowdyHead",
            "Eden",
            "HayPt",
            "Mackay",
            "PortKembla",
            "Tsv",
            "Mandurah",
        ],
        description="Names of output stations.",
    )
    xouts: Optional[list] = Field(
        [
            150.31972,
            153.63166,
            153.745,
            145.715167,
            153.27722,
            152.85333,
            150.15833,
            149.31025,
            149.5467,
            151.01667,
            147.059333,
            115.572227,
        ],
        description="Longitude coordinates of output stations.",
    )
    youts: Optional[list] = Field(
        [
            -35.75528,
            -27.48716,
            -28.67167,
            -16.7305,
            -30.34361,
            -31.82694,
            -37.175,
            -21.2715,
            -21.037333,
            -34.46694,
            -19.159167,
            -32.452787,
        ],
        description="Latitude coordinates of output stations.",
    )
    cutoff: Optional[float] = Field(
        0.0,
        description="Cutoff frequency (Hz) for each station, consistent with buoys.",
    )
    lsp1d: Optional[bool] = Field(
        True, description="Enable 1D spectral station output."
    )
    lsp2d: Optional[bool] = Field(
        True, description="Enable 2D spectral station output."
    )
    lsigmax: Optional[bool] = Field(
        True,
        description="Adjust the cut-off frequency for the output (e.g., consistent with buoy cut-off frequency).",
    )
    ac: Optional[bool] = Field(True, description="Output spectrum.")
    wk: Optional[bool] = Field(False, description="Output variable WK.")
    acout_1d: Optional[bool] = Field(False, description="Output variable ACOUT_1D.")
    acout_2d: Optional[bool] = Field(False, description="Output variable ACOUT_2D.")
    hs: Optional[bool] = Field(True, description="Output significant wave height.")
    tm01: Optional[bool] = Field(True, description="Output mean period.")
    tm02: Optional[bool] = Field(True, description="Output zero-crossing mean period.")
    klm: Optional[bool] = Field(False, description="Output mean wave number.")
    wlm: Optional[bool] = Field(False, description="Output mean wave length.")
    etotc: Optional[bool] = Field(False, description="Output variable ETOTC.")
    etots: Optional[bool] = Field(False, description="Output variable ETOTS.")
    dm: Optional[bool] = Field(True, description="Output mean wave direction.")
    dspr: Optional[bool] = Field(True, description="Output directional spreading.")
    tppd: Optional[bool] = Field(True, description="Output discrete peak period.")
    tpp: Optional[bool] = Field(True, description="Output peak period.")
    cpp: Optional[bool] = Field(False, description="Output variable CPP.")
    wnpp: Optional[bool] = Field(False, description="Output peak wave number.")
    cgpp: Optional[bool] = Field(False, description="Output peak group speed.")
    kpp: Optional[bool] = Field(False, description="Output peak wave number.")
    lpp: Optional[bool] = Field(False, description="Output peak wavelength.")
    peakd: Optional[bool] = Field(True, description="Output peak direction.")
    peakdspr: Optional[bool] = Field(
        True, description="Output peak directional spreading."
    )
    dpeak: Optional[bool] = Field(False, description="Output variable DPEAK.")
    ubot: Optional[bool] = Field(False, description="Output variable UBOT.")
    orbital: Optional[bool] = Field(False, description="Output orbital velocity.")
    botexper: Optional[bool] = Field(
        False, description="Output bottom excursion period."
    )
    tmbot: Optional[bool] = Field(False, description="Output variable TMBOT.")
    ursell: Optional[bool] = Field(False, description="Output Ursell number.")
    ufric: Optional[bool] = Field(False, description="Output air friction velocity.")
    z0: Optional[bool] = Field(False, description="Output air roughness length.")
    alpha_ch: Optional[bool] = Field(
        False, description="Output Charnock coefficient for air."
    )
    windx: Optional[bool] = Field(True, description="Output wind in X direction.")
    windy: Optional[bool] = Field(True, description="Output wind in Y direction.")
    cd: Optional[bool] = Field(False, description="Output drag coefficient.")
    currtx: Optional[bool] = Field(False, description="Output current in X direction.")
    currty: Optional[bool] = Field(False, description="Output current in Y direction.")
    watlev: Optional[bool] = Field(False, description="Output water level.")
    watlevold: Optional[bool] = Field(
        False, description="Output water level at previous time step."
    )
    depdt: Optional[bool] = Field(
        False, description="Output change of water level in time."
    )
    dep: Optional[bool] = Field(True, description="Output depth.")
    tauw: Optional[bool] = Field(
        False, description="Output surface stress from the wave."
    )
    tauhf: Optional[bool] = Field(
        False, description="Output high frequency surface stress."
    )
    tautot: Optional[bool] = Field(False, description="Output total surface stress.")
    stokessurfx: Optional[bool] = Field(
        True, description="Output surface Stokes drift in X direction."
    )
    stokessurfy: Optional[bool] = Field(
        True, description="Output surface Stokes drift in Y direction."
    )
    stokesbarox: Optional[bool] = Field(
        False, description="Output barotropic Stokes drift in X direction."
    )
    stokesbaroy: Optional[bool] = Field(
        False, description="Output barotropic Stokes drift in Y direction."
    )
    rsxx: Optional[bool] = Field(False, description="Output RSXX potential of LH.")
    rsxy: Optional[bool] = Field(False, description="Output RSXY potential of LH.")
    rsyy: Optional[bool] = Field(False, description="Output RSYY potential of LH.")
    cfl1: Optional[bool] = Field(False, description="Output CFL number 1.")
    cfl2: Optional[bool] = Field(False, description="Output CFL number 2.")
    cfl3: Optional[bool] = Field(False, description="Output CFL number 3.")

    @field_validator("begtc")
    def validate_begtc(cls, v):
        import re

        if not re.match(r"^\d{8}\.\d{6}$", v):
            raise ValueError("BEGTC must be in format yyyymmdd.hhmmss")
        return v

    @field_validator("deltc")
    def validate_deltc(cls, v):
        if v <= 0:
            raise ValueError("DELTC must be a positive integer")
        return v

    @field_validator("unitc")
    def validate_unitc(cls, v):
        if v != "SEC":
            raise ValueError("UNITC must be SEC")
        return v

    @field_validator("endtc")
    def validate_endtc(cls, v):
        import re

        if not re.match(r"^\d{8}\.\d{6}$", v):
            raise ValueError("ENDTC must be in format yyyymmdd.hhmmss")
        return v

    @field_validator("definetc")
    def validate_definetc(cls, v):
        if v != -1 and v < 0:
            raise ValueError("DEFINETC must be -1 or a non-negative integer")
        return v

    @field_validator("outstyle")
    def validate_outstyle(cls, v):
        if v not in ["NO", "STE", "NC"]:
            raise ValueError("OUTSTYLE must be NO, STE, or NC")
        return v

    @field_validator("multipleout")
    def validate_multipleout(cls, v):
        if v not in [0, 1]:
            raise ValueError("MULTIPLEOUT must be 0 or 1")
        return v

    @field_validator("iouts")
    def validate_iouts(cls, v):
        if v <= 0:
            raise ValueError("IOUTS must be a positive integer")
        return v

    @field_validator("nouts")
    def validate_nouts(cls, v, info):
        if len(v) != info.data.get("iouts"):
            raise ValueError("Number of NOUTS must match IOUTS")
        return v

    @field_validator("xouts")
    def validate_xouts(cls, v, info):
        if len(v) != info.data.get("iouts"):
            raise ValueError("Number of XOUTS must match IOUTS")
        return v

    @field_validator("youts")
    def validate_youts(cls, v, info):
        if len(v) != info.data.get("iouts"):
            raise ValueError("Number of YOUTS must match IOUTS")
        return v

    @field_validator("cutoff")
    def validate_cutoff(cls, v):
        if v < 0:
            raise ValueError("CUTOFF must be non-negative")
        return v

Attributes

begtc class-attribute instance-attribute

begtc: Optional[str] = Field('20200101.000000', description="Start simulation time in 'yyyymmdd.hhmmss' format. Must fit the simulation time, otherwise no output is generated. Defaults to PROC%BEGTC if not specified.")

deltc class-attribute instance-attribute

deltc: Optional[int] = Field(3600, description='Time step for output in seconds. If smaller than simulation time step, the latter is used. Used for better 1D and 2D spectra analysis.')

unitc class-attribute instance-attribute

unitc: Optional[str] = Field('SEC', description="Time unit for DELTC. Only 'SEC' is currently supported.")

endtc class-attribute instance-attribute

endtc: Optional[str] = Field('20200201.000000', description="Stop time for simulation in 'yyyymmdd.hhmmss' format. Defaults to PROC%ENDC if not specified.")

definetc class-attribute instance-attribute

definetc: Optional[int] = Field(-1, description='Time for definition of station files in seconds. If negative or unset, only one file is generated. Otherwise, it defines the interval for creating output files (e.g., 86400 for daily files).')

outstyle class-attribute instance-attribute

outstyle: Optional[str] = Field('NC', description="Output option. 'NO' for no output, 'STE' for classic station output (default), 'NC' for netCDF output.")

multipleout class-attribute instance-attribute

multipleout: Optional[int] = Field(0, description='Output file configuration. 0 for a single netCDF file using MPI_reduce (default), 1 for separate netCDF files for each process.')

use_single_out class-attribute instance-attribute

use_single_out: Optional[bool] = Field(True, description='Use single precision in the output of model variables. True by default.')

paramwrite class-attribute instance-attribute

paramwrite: Optional[bool] = Field(True, description='Write the physical parametrization and chosen numerical method in the netCDF file. True by default.')

fileout class-attribute instance-attribute

fileout: Optional[str] = Field('wwm_sta.nc', description='Output file name (not used).')

loutiter class-attribute instance-attribute

loutiter: Optional[bool] = Field(False, description='Boolean flag for output iteration.')

iouts class-attribute instance-attribute

iouts: Optional[int] = Field(12, description='Number of output stations.')

nouts class-attribute instance-attribute

nouts: Optional[list[str]] = Field(['BatemansBay', 'Brisbane', 'ByronBay', 'Cairns', 'CoffsHbr', 'CrowdyHead', 'Eden', 'HayPt', 'Mackay', 'PortKembla', 'Tsv', 'Mandurah'], description='Names of output stations.')

xouts class-attribute instance-attribute

xouts: Optional[list] = Field([150.31972, 153.63166, 153.745, 145.715167, 153.27722, 152.85333, 150.15833, 149.31025, 149.5467, 151.01667, 147.059333, 115.572227], description='Longitude coordinates of output stations.')

youts class-attribute instance-attribute

youts: Optional[list] = Field([-35.75528, -27.48716, -28.67167, -16.7305, -30.34361, -31.82694, -37.175, -21.2715, -21.037333, -34.46694, -19.159167, -32.452787], description='Latitude coordinates of output stations.')

cutoff class-attribute instance-attribute

cutoff: Optional[float] = Field(0.0, description='Cutoff frequency (Hz) for each station, consistent with buoys.')

lsp1d class-attribute instance-attribute

lsp1d: Optional[bool] = Field(True, description='Enable 1D spectral station output.')

lsp2d class-attribute instance-attribute

lsp2d: Optional[bool] = Field(True, description='Enable 2D spectral station output.')

lsigmax class-attribute instance-attribute

lsigmax: Optional[bool] = Field(True, description='Adjust the cut-off frequency for the output (e.g., consistent with buoy cut-off frequency).')

ac class-attribute instance-attribute

ac: Optional[bool] = Field(True, description='Output spectrum.')

wk class-attribute instance-attribute

wk: Optional[bool] = Field(False, description='Output variable WK.')

acout_1d class-attribute instance-attribute

acout_1d: Optional[bool] = Field(False, description='Output variable ACOUT_1D.')

acout_2d class-attribute instance-attribute

acout_2d: Optional[bool] = Field(False, description='Output variable ACOUT_2D.')

hs class-attribute instance-attribute

hs: Optional[bool] = Field(True, description='Output significant wave height.')

tm01 class-attribute instance-attribute

tm01: Optional[bool] = Field(True, description='Output mean period.')

tm02 class-attribute instance-attribute

tm02: Optional[bool] = Field(True, description='Output zero-crossing mean period.')

klm class-attribute instance-attribute

klm: Optional[bool] = Field(False, description='Output mean wave number.')

wlm class-attribute instance-attribute

wlm: Optional[bool] = Field(False, description='Output mean wave length.')

etotc class-attribute instance-attribute

etotc: Optional[bool] = Field(False, description='Output variable ETOTC.')

etots class-attribute instance-attribute

etots: Optional[bool] = Field(False, description='Output variable ETOTS.')

dm class-attribute instance-attribute

dm: Optional[bool] = Field(True, description='Output mean wave direction.')

dspr class-attribute instance-attribute

dspr: Optional[bool] = Field(True, description='Output directional spreading.')

tppd class-attribute instance-attribute

tppd: Optional[bool] = Field(True, description='Output discrete peak period.')

tpp class-attribute instance-attribute

tpp: Optional[bool] = Field(True, description='Output peak period.')

cpp class-attribute instance-attribute

cpp: Optional[bool] = Field(False, description='Output variable CPP.')

wnpp class-attribute instance-attribute

wnpp: Optional[bool] = Field(False, description='Output peak wave number.')

cgpp class-attribute instance-attribute

cgpp: Optional[bool] = Field(False, description='Output peak group speed.')

kpp class-attribute instance-attribute

kpp: Optional[bool] = Field(False, description='Output peak wave number.')

lpp class-attribute instance-attribute

lpp: Optional[bool] = Field(False, description='Output peak wavelength.')

peakd class-attribute instance-attribute

peakd: Optional[bool] = Field(True, description='Output peak direction.')

peakdspr class-attribute instance-attribute

peakdspr: Optional[bool] = Field(True, description='Output peak directional spreading.')

dpeak class-attribute instance-attribute

dpeak: Optional[bool] = Field(False, description='Output variable DPEAK.')

ubot class-attribute instance-attribute

ubot: Optional[bool] = Field(False, description='Output variable UBOT.')

orbital class-attribute instance-attribute

orbital: Optional[bool] = Field(False, description='Output orbital velocity.')

botexper class-attribute instance-attribute

botexper: Optional[bool] = Field(False, description='Output bottom excursion period.')

tmbot class-attribute instance-attribute

tmbot: Optional[bool] = Field(False, description='Output variable TMBOT.')

ursell class-attribute instance-attribute

ursell: Optional[bool] = Field(False, description='Output Ursell number.')

ufric class-attribute instance-attribute

ufric: Optional[bool] = Field(False, description='Output air friction velocity.')

z0 class-attribute instance-attribute

z0: Optional[bool] = Field(False, description='Output air roughness length.')

alpha_ch class-attribute instance-attribute

alpha_ch: Optional[bool] = Field(False, description='Output Charnock coefficient for air.')

windx class-attribute instance-attribute

windx: Optional[bool] = Field(True, description='Output wind in X direction.')

windy class-attribute instance-attribute

windy: Optional[bool] = Field(True, description='Output wind in Y direction.')

cd class-attribute instance-attribute

cd: Optional[bool] = Field(False, description='Output drag coefficient.')

currtx class-attribute instance-attribute

currtx: Optional[bool] = Field(False, description='Output current in X direction.')

currty class-attribute instance-attribute

currty: Optional[bool] = Field(False, description='Output current in Y direction.')

watlev class-attribute instance-attribute

watlev: Optional[bool] = Field(False, description='Output water level.')

watlevold class-attribute instance-attribute

watlevold: Optional[bool] = Field(False, description='Output water level at previous time step.')

depdt class-attribute instance-attribute

depdt: Optional[bool] = Field(False, description='Output change of water level in time.')

dep class-attribute instance-attribute

dep: Optional[bool] = Field(True, description='Output depth.')

tauw class-attribute instance-attribute

tauw: Optional[bool] = Field(False, description='Output surface stress from the wave.')

tauhf class-attribute instance-attribute

tauhf: Optional[bool] = Field(False, description='Output high frequency surface stress.')

tautot class-attribute instance-attribute

tautot: Optional[bool] = Field(False, description='Output total surface stress.')

stokessurfx class-attribute instance-attribute

stokessurfx: Optional[bool] = Field(True, description='Output surface Stokes drift in X direction.')

stokessurfy class-attribute instance-attribute

stokessurfy: Optional[bool] = Field(True, description='Output surface Stokes drift in Y direction.')

stokesbarox class-attribute instance-attribute

stokesbarox: Optional[bool] = Field(False, description='Output barotropic Stokes drift in X direction.')

stokesbaroy class-attribute instance-attribute

stokesbaroy: Optional[bool] = Field(False, description='Output barotropic Stokes drift in Y direction.')

rsxx class-attribute instance-attribute

rsxx: Optional[bool] = Field(False, description='Output RSXX potential of LH.')

rsxy class-attribute instance-attribute

rsxy: Optional[bool] = Field(False, description='Output RSXY potential of LH.')

rsyy class-attribute instance-attribute

rsyy: Optional[bool] = Field(False, description='Output RSYY potential of LH.')

cfl1 class-attribute instance-attribute

cfl1: Optional[bool] = Field(False, description='Output CFL number 1.')

cfl2 class-attribute instance-attribute

cfl2: Optional[bool] = Field(False, description='Output CFL number 2.')

cfl3 class-attribute instance-attribute

cfl3: Optional[bool] = Field(False, description='Output CFL number 3.')

Functions

validate_begtc

validate_begtc(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("begtc")
def validate_begtc(cls, v):
    import re

    if not re.match(r"^\d{8}\.\d{6}$", v):
        raise ValueError("BEGTC must be in format yyyymmdd.hhmmss")
    return v

validate_deltc

validate_deltc(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("deltc")
def validate_deltc(cls, v):
    if v <= 0:
        raise ValueError("DELTC must be a positive integer")
    return v

validate_unitc

validate_unitc(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("unitc")
def validate_unitc(cls, v):
    if v != "SEC":
        raise ValueError("UNITC must be SEC")
    return v

validate_endtc

validate_endtc(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("endtc")
def validate_endtc(cls, v):
    import re

    if not re.match(r"^\d{8}\.\d{6}$", v):
        raise ValueError("ENDTC must be in format yyyymmdd.hhmmss")
    return v

validate_definetc

validate_definetc(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("definetc")
def validate_definetc(cls, v):
    if v != -1 and v < 0:
        raise ValueError("DEFINETC must be -1 or a non-negative integer")
    return v

validate_outstyle

validate_outstyle(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("outstyle")
def validate_outstyle(cls, v):
    if v not in ["NO", "STE", "NC"]:
        raise ValueError("OUTSTYLE must be NO, STE, or NC")
    return v

validate_multipleout

validate_multipleout(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("multipleout")
def validate_multipleout(cls, v):
    if v not in [0, 1]:
        raise ValueError("MULTIPLEOUT must be 0 or 1")
    return v

validate_iouts

validate_iouts(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("iouts")
def validate_iouts(cls, v):
    if v <= 0:
        raise ValueError("IOUTS must be a positive integer")
    return v

validate_nouts

validate_nouts(v, info)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("nouts")
def validate_nouts(cls, v, info):
    if len(v) != info.data.get("iouts"):
        raise ValueError("Number of NOUTS must match IOUTS")
    return v

validate_xouts

validate_xouts(v, info)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("xouts")
def validate_xouts(cls, v, info):
    if len(v) != info.data.get("iouts"):
        raise ValueError("Number of XOUTS must match IOUTS")
    return v

validate_youts

validate_youts(v, info)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("youts")
def validate_youts(cls, v, info):
    if len(v) != info.data.get("iouts"):
        raise ValueError("Number of YOUTS must match IOUTS")
    return v

validate_cutoff

validate_cutoff(v)
Source code in rompy_schism/namelists/wwminput.py
@field_validator("cutoff")
def validate_cutoff(cls, v):
    if v < 0:
        raise ValueError("CUTOFF must be non-negative")
    return v

Wwminput

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/wwminput.py
class Wwminput(NamelistBaseModel):
    proc: Optional[Proc] = Field(default_factory=Proc)
    coupl: Optional[Coupl] = Field(default_factory=Coupl)
    grid: Optional[Grid] = Field(default_factory=Grid)
    init: Optional[Init] = Field(default_factory=Init)
    bouc: Optional[Bouc] = Field(default_factory=Bouc)
    wind: Optional[Wind] = Field(default_factory=Wind)
    curr: Optional[Curr] = Field(default_factory=Curr)
    walv: Optional[Walv] = Field(default_factory=Walv)
    engs: Optional[Engs] = Field(default_factory=Engs)
    nums: Optional[Nums] = Field(default_factory=Nums)
    history: Optional[History] = Field(default_factory=History)
    station: Optional[Station] = Field(default_factory=Station)
    hotfile: Optional[Hotfile] = Field(default_factory=Hotfile)
    petscoptions: Optional[Petscoptions] = Field(default_factory=Petscoptions)
    nesting: Optional[Nesting] = Field(default_factory=Nesting)

    def render(self) -> str:
        # Needs an additional backslash due to to uknown issue with schism
        # All sameple namelists have this feature
        return super().render() + "\n" + "/"

Attributes

proc class-attribute instance-attribute

proc: Optional[Proc] = Field(default_factory=Proc)

coupl class-attribute instance-attribute

coupl: Optional[Coupl] = Field(default_factory=Coupl)

grid class-attribute instance-attribute

grid: Optional[Grid] = Field(default_factory=Grid)

init class-attribute instance-attribute

init: Optional[Init] = Field(default_factory=Init)

bouc class-attribute instance-attribute

bouc: Optional[Bouc] = Field(default_factory=Bouc)

wind class-attribute instance-attribute

wind: Optional[Wind] = Field(default_factory=Wind)

curr class-attribute instance-attribute

curr: Optional[Curr] = Field(default_factory=Curr)

walv class-attribute instance-attribute

walv: Optional[Walv] = Field(default_factory=Walv)

engs class-attribute instance-attribute

engs: Optional[Engs] = Field(default_factory=Engs)

nums class-attribute instance-attribute

nums: Optional[Nums] = Field(default_factory=Nums)

history class-attribute instance-attribute

history: Optional[History] = Field(default_factory=History)

station class-attribute instance-attribute

station: Optional[Station] = Field(default_factory=Station)

hotfile class-attribute instance-attribute

hotfile: Optional[Hotfile] = Field(default_factory=Hotfile)

petscoptions class-attribute instance-attribute

petscoptions: Optional[Petscoptions] = Field(default_factory=Petscoptions)

nesting class-attribute instance-attribute

nesting: Optional[Nesting] = Field(default_factory=Nesting)

Functions

render

render() -> str
Source code in rompy_schism/namelists/wwminput.py
def render(self) -> str:
    # Needs an additional backslash due to to uknown issue with schism
    # All sameple namelists have this feature
    return super().render() + "\n" + "/"

NML

This is the full namelist object that is the container for all the other namelist objects.

NML

Bases: NamelistBaseModel

Source code in rompy_schism/namelists/schism.py
class NML(NamelistBaseModel):
    param: Optional[Param] = Field(description="Model paramaters", default=None)
    ice: Optional[Ice] = Field(description="Ice model parameters", default=None)
    icm: Optional[Icm] = Field(description="Icm model parameters", default=None)
    mice: Optional[Mice] = Field(description="Mice model parameters", default=None)
    sediment: Optional[Sediment] = Field(
        description="Sediment model parameters", default=None
    )
    cosine: Optional[Cosine] = Field(
        description="Cosine model parameters", default=None
    )
    wwminput: Optional[Wwminput] = Field(
        description="Wave model input parameters", default=None
    )

    @model_serializer
    def serialize_model(self, **kwargs):
        """Custom serializer to handle proper serialization of namelist components."""
        result = {}

        # Include only non-None fields in the serialized output
        for field_name in self.model_fields:
            value = getattr(self, field_name, None)
            if value is not None:
                # Ensure we're returning the model object, not a dict
                if hasattr(value, "model_dump"):
                    # This ensures we maintain the model instance for proper serialization
                    result[field_name] = value
                else:
                    result[field_name] = value

        return result

    def update_times(self, period=TimeRange):
        """
        This class is used to set consistent time parameters in a group component by
        redefining existing `times` component attribute based on the `period` field.

        """

        update = {
            "param": {
                "core": {
                    "rnday": period.duration.total_seconds() / 86400,
                },
                "opt": {
                    "start_year": period.start.year,
                    "start_month": period.start.month,
                    "start_day": period.start.day,
                    "start_hour": period.start.hour,
                },
            }
        }

        date_format = "%Y%m%d.%H%M%S"
        if hasattr(self, "wwminput"):  # TODO change this check to the actual flag value
            # TODO these are currently all the same, but they could be different
            update.update(
                {
                    "wwminput": {
                        "proc": {
                            "begtc": period.start.strftime(date_format),
                            "endtc": period.end.strftime(date_format),
                        },
                        "wind": {
                            "begtc": period.start.strftime(date_format),
                            "endtc": period.end.strftime(date_format),
                        },
                        "curr": {
                            "begtc": period.start.strftime(date_format),
                            "endtc": period.end.strftime(date_format),
                        },
                        "walv": {
                            "begtc": period.start.strftime(date_format),
                            "endtc": period.end.strftime(date_format),
                        },
                        "history": {
                            "begtc": period.start.strftime(date_format),
                            "endtc": period.end.strftime(date_format),
                        },
                        "bouc": {
                            "begtc": period.start.strftime(date_format),
                            "endtc": period.end.strftime(date_format),
                        },
                        "station": {
                            "begtc": period.start.strftime(date_format),
                            "endtc": period.end.strftime(date_format),
                        },
                        "hotfile": {
                            "begtc": period.start.strftime(date_format),
                            "endtc": period.end.strftime(date_format),
                        },
                    }
                }
            )
        self.update(update)

    def update_data_sources(self, datasources: dict):
        """Update the data sources in the namelist based on rompy data preparation."""
        update = {}
        if ("wave" in datasources) and (datasources["wave"] is not None):
            if hasattr(self, "wwminput") and (
                self.wwminput is not None
            ):  # TODO change this check to the actual flag value
                if self.wwminput.bouc is not None:
                    logger.warn(
                        "Overwriting existing wave data source specified in namelist with rompy generated data"
                    )
                update.update(
                    {
                        "wwminput": {
                            "bouc": {
                                "filewave": datasources["wave"].name,
                            },
                        }
                    }
                )
        if ("atmos" in datasources) and (datasources["atmos"] is not None):
            if self.param.opt.nws != 2:
                logger.warn(
                    f"Overwriting param nws value of {self.param.opt.nws} to 2 to use rompy generated sflux data"
                )
                update.update(
                    {
                        "param": {
                            "opt": {"nws": 2},
                        }
                    }
                )
        self.update(update)

    def write_nml(self, workdir: Path):
        for nml in [
            "param",
            "ice",
            "icm",
            "mice",
            "sediment",
            "cosine",
            "wwminput",
        ]:
            attr = getattr(self, nml)
            if attr is not None:
                attr.write_nml(workdir)

    def _format_value(self, obj):
        """Custom formatter for NML values.

        This method provides special formatting for specific types used in
        NML such as namelist components and parameters.

        Args:
            obj: The object to format

        Returns:
            A formatted string or None to use default formatting
        """
        # Import specific types and formatting utilities
        from rompy.formatting import get_formatted_header_footer
        from rompy.logging import LoggingConfig

        # Get ASCII mode setting from LoggingConfig
        logging_config = LoggingConfig()
        USE_ASCII_ONLY = logging_config.use_ascii

        # Format NML (self-formatting)
        if isinstance(obj, NML):
            header, footer, bullet = get_formatted_header_footer(
                title="SCHISM NAMELIST CONFIGURATION", use_ascii=USE_ASCII_ONLY
            )

            lines = [header]

            # List active namelist components
            components = {}
            if hasattr(obj, "param") and obj.param is not None:
                components["Parameters"] = type(obj.param).__name__
            if hasattr(obj, "ice") and obj.ice is not None:
                components["Ice"] = type(obj.ice).__name__
            if hasattr(obj, "icm") and obj.icm is not None:
                components["ICM"] = type(obj.icm).__name__
            if hasattr(obj, "mice") and obj.mice is not None:
                components["MICE"] = type(obj.mice).__name__
            if hasattr(obj, "sediment") and obj.sediment is not None:
                components["Sediment"] = type(obj.sediment).__name__
            if hasattr(obj, "cosine") and obj.cosine is not None:
                components["CoSiNE"] = type(obj.cosine).__name__
            if hasattr(obj, "wwminput") and obj.wwminput is not None:
                components["Wave"] = type(obj.wwminput).__name__

            for comp_name, comp_type in components.items():
                lines.append(f"  {bullet} {comp_name}: {comp_type}")

            if not components:
                lines.append(f"  {bullet} No modules configured")

            lines.append(footer)
            return "\n".join(lines)

        # Format Param class
        from .param import Param

        if isinstance(obj, Param):
            header, footer, bullet = get_formatted_header_footer(
                title="SCHISM PARAMETERS", use_ascii=USE_ASCII_ONLY
            )

            lines = [header]

            # Add key parameter information
            if hasattr(obj, "core") and obj.core is not None:
                if hasattr(obj.core, "rnday"):
                    lines.append(f"  {bullet} Run duration: {obj.core.rnday} days")
                if hasattr(obj.core, "dt"):
                    lines.append(f"  {bullet} Time step: {obj.core.dt} seconds")

            if hasattr(obj, "opt") and obj.opt is not None:
                if hasattr(obj.opt, "nws"):
                    lines.append(f"  {bullet} Wind forcing: NWS={obj.opt.nws}")
                if hasattr(obj.opt, "ihot"):
                    hotstart = "Enabled" if obj.opt.ihot == 1 else "Disabled"
                    lines.append(f"  {bullet} Hotstart: {hotstart}")

            lines.append(footer)
            return "\n".join(lines)

        # Use the new formatting framework
        from rompy.formatting import format_value

        return format_value(obj)

Attributes

param class-attribute instance-attribute

param: Optional[Param] = Field(description='Model paramaters', default=None)

ice class-attribute instance-attribute

ice: Optional[Ice] = Field(description='Ice model parameters', default=None)

icm class-attribute instance-attribute

icm: Optional[Icm] = Field(description='Icm model parameters', default=None)

mice class-attribute instance-attribute

mice: Optional[Mice] = Field(description='Mice model parameters', default=None)

sediment class-attribute instance-attribute

sediment: Optional[Sediment] = Field(description='Sediment model parameters', default=None)

cosine class-attribute instance-attribute

cosine: Optional[Cosine] = Field(description='Cosine model parameters', default=None)

wwminput class-attribute instance-attribute

wwminput: Optional[Wwminput] = Field(description='Wave model input parameters', default=None)

Functions

serialize_model

serialize_model(**kwargs)

Custom serializer to handle proper serialization of namelist components.

Source code in rompy_schism/namelists/schism.py
@model_serializer
def serialize_model(self, **kwargs):
    """Custom serializer to handle proper serialization of namelist components."""
    result = {}

    # Include only non-None fields in the serialized output
    for field_name in self.model_fields:
        value = getattr(self, field_name, None)
        if value is not None:
            # Ensure we're returning the model object, not a dict
            if hasattr(value, "model_dump"):
                # This ensures we maintain the model instance for proper serialization
                result[field_name] = value
            else:
                result[field_name] = value

    return result

update_times

update_times(period=TimeRange)

This class is used to set consistent time parameters in a group component by redefining existing times component attribute based on the period field.

Source code in rompy_schism/namelists/schism.py
def update_times(self, period=TimeRange):
    """
    This class is used to set consistent time parameters in a group component by
    redefining existing `times` component attribute based on the `period` field.

    """

    update = {
        "param": {
            "core": {
                "rnday": period.duration.total_seconds() / 86400,
            },
            "opt": {
                "start_year": period.start.year,
                "start_month": period.start.month,
                "start_day": period.start.day,
                "start_hour": period.start.hour,
            },
        }
    }

    date_format = "%Y%m%d.%H%M%S"
    if hasattr(self, "wwminput"):  # TODO change this check to the actual flag value
        # TODO these are currently all the same, but they could be different
        update.update(
            {
                "wwminput": {
                    "proc": {
                        "begtc": period.start.strftime(date_format),
                        "endtc": period.end.strftime(date_format),
                    },
                    "wind": {
                        "begtc": period.start.strftime(date_format),
                        "endtc": period.end.strftime(date_format),
                    },
                    "curr": {
                        "begtc": period.start.strftime(date_format),
                        "endtc": period.end.strftime(date_format),
                    },
                    "walv": {
                        "begtc": period.start.strftime(date_format),
                        "endtc": period.end.strftime(date_format),
                    },
                    "history": {
                        "begtc": period.start.strftime(date_format),
                        "endtc": period.end.strftime(date_format),
                    },
                    "bouc": {
                        "begtc": period.start.strftime(date_format),
                        "endtc": period.end.strftime(date_format),
                    },
                    "station": {
                        "begtc": period.start.strftime(date_format),
                        "endtc": period.end.strftime(date_format),
                    },
                    "hotfile": {
                        "begtc": period.start.strftime(date_format),
                        "endtc": period.end.strftime(date_format),
                    },
                }
            }
        )
    self.update(update)

update_data_sources

update_data_sources(datasources: dict)

Update the data sources in the namelist based on rompy data preparation.

Source code in rompy_schism/namelists/schism.py
def update_data_sources(self, datasources: dict):
    """Update the data sources in the namelist based on rompy data preparation."""
    update = {}
    if ("wave" in datasources) and (datasources["wave"] is not None):
        if hasattr(self, "wwminput") and (
            self.wwminput is not None
        ):  # TODO change this check to the actual flag value
            if self.wwminput.bouc is not None:
                logger.warn(
                    "Overwriting existing wave data source specified in namelist with rompy generated data"
                )
            update.update(
                {
                    "wwminput": {
                        "bouc": {
                            "filewave": datasources["wave"].name,
                        },
                    }
                }
            )
    if ("atmos" in datasources) and (datasources["atmos"] is not None):
        if self.param.opt.nws != 2:
            logger.warn(
                f"Overwriting param nws value of {self.param.opt.nws} to 2 to use rompy generated sflux data"
            )
            update.update(
                {
                    "param": {
                        "opt": {"nws": 2},
                    }
                }
            )
    self.update(update)

write_nml

write_nml(workdir: Path)
Source code in rompy_schism/namelists/schism.py
def write_nml(self, workdir: Path):
    for nml in [
        "param",
        "ice",
        "icm",
        "mice",
        "sediment",
        "cosine",
        "wwminput",
    ]:
        attr = getattr(self, nml)
        if attr is not None:
            attr.write_nml(workdir)

Config Object

SCHISMConfig

Bases: BaseConfig

Source code in rompy_schism/config.py
class SCHISMConfig(BaseConfig):
    model_type: Literal["schism"] = Field(
        "schism", description="The model type for SCHISM."
    )
    grid: SCHISMGrid = Field(description="The model grid")
    data: Optional[SCHISMData] = Field(None, description="Model inputs")
    nml: Optional[NML] = Field(
        default_factory=lambda: NML(param=Param()), description="The namelist"
    )
    template: Optional[str] = Field(
        description="The path to the model template",
        default=SCHISM_TEMPLATE,
    )

    # add a validator that checks that nml.param.ihot is 1 if data.hotstart is not none
    @model_validator(mode="after")
    def check_hotstart(self):
        if (
            self.data is not None
            and hasattr(self.data, "hotstart")
            and getattr(self.data, "hotstart", None) is not None
            and self.nml is not None
            and self.nml.param is not None
            and self.nml.param.opt is not None
        ):
            self.nml.param.opt.ihot = 1
        return self

    @model_serializer
    def serialize_model(self, **kwargs):
        """Custom serializer to handle proper serialization of nested components."""
        from rompy_schism.grid import GR3Generator

        result = {}

        # Explicitly handle required fields
        result["model_type"] = self.model_type

        # Handle grid separately to process GR3Generator objects
        if self.grid is not None:
            grid_dict = {}
            for field_name in self.grid.model_fields:
                value = getattr(self.grid, field_name, None)

                # Special handling for GR3Generator objects
                if value is not None and isinstance(value, GR3Generator):
                    # For GR3Generator objects, extract just the value field
                    grid_dict[field_name] = value.value
                elif value is not None and not field_name.startswith("_"):
                    grid_dict[field_name] = value

            result["grid"] = grid_dict

        # Add optional fields that are not None
        if self.data is not None:
            result["data"] = self.data

        if self.nml is not None:
            result["nml"] = self.nml

        if self.template is not None:
            result["template"] = self.template

        return result

    # Enable arbitrary types and validation from instances in Pydantic v2
    model_config = ConfigDict(arbitrary_types_allowed=True, from_attributes=True)

    # Add data visualization methods
    # Atmospheric (sflux) plotting
    plot_sflux_spatial = plot_sflux_spatial
    plot_sflux_timeseries = plot_sflux_timeseries

    # Boundary data plotting
    plot_boundary_points = plot_boundary_points
    plot_boundary_timeseries = plot_boundary_timeseries
    plot_boundary_profile = plot_boundary_profile

    # Tidal data plotting
    plot_tidal_boundaries = plot_tidal_boundaries
    plot_tidal_stations = plot_tidal_stations
    plot_tidal_rose = plot_tidal_rose
    plot_tidal_dataset = plot_tidal_dataset

    def __call__(self, runtime) -> str:
        from rompy.formatting import ARROW, log_box

        # Grid generation section
        log_box(
            title="GENERATING GRID FILES",
            logger=logger,
            add_empty_line=False,
        )
        logger.info(f"{ARROW} Grid type: {type(self.grid).__name__}")

        # Log grid details
        if hasattr(self.grid, "hgrid") and self.grid.hgrid is not None:
            hgrid_source = str(getattr(self.grid.hgrid, "source", self.grid.hgrid))
            if len(hgrid_source) > 60:
                hgrid_source = "..." + hgrid_source[-57:]
            logger.info(f"{ARROW} Horizontal grid: {hgrid_source}")

        if hasattr(self.grid, "vgrid") and self.grid.vgrid is not None:
            vgrid_type = type(self.grid.vgrid).__name__
            logger.info(f"{ARROW} Vertical grid: {vgrid_type}")

        # Generate grid files
        self.grid.get(runtime.staging_dir)
        logger.info(f"{ARROW} Grid files generated successfully")

        # Data processing section
        if self.data is not None:
            log_box(
                title="PROCESSING INPUT DATA",
                logger=logger,
                add_empty_line=False,
            )

            # Log data components
            data_components = []
            if hasattr(self.data, "atmos") and self.data.atmos is not None:
                data_components.append("Atmospheric")
            if hasattr(self.data, "wave") and self.data.wave is not None:
                data_components.append("Wave")
            if (
                hasattr(self.data, "boundary_conditions")
                and self.data.boundary_conditions is not None
            ):
                data_components.append("Boundary Conditions")

            if data_components:
                logger.info(f"{ARROW} Components: {', '.join(data_components)}")

            # Process data
            data_results = self.data.get(
                destdir=runtime.staging_dir, grid=self.grid, time=runtime.period
            )

            if self.nml is not None:
                self.nml.update_data_sources(data_results)

            logger.info(f"{ARROW} Input data processed successfully")

        # Namelist configuration section
        if self.nml is not None:
            log_box(
                title="CONFIGURING NAMELISTS",
                logger=logger,
                add_empty_line=False,
            )

            # Log active modules
            active_modules = []
            if hasattr(self.nml, "param") and self.nml.param is not None:
                active_modules.append("Parameters")
            if hasattr(self.nml, "ice") and self.nml.ice is not None:
                active_modules.append("Ice")
            if hasattr(self.nml, "icm") and self.nml.icm is not None:
                active_modules.append("ICM")
            if hasattr(self.nml, "sediment") and self.nml.sediment is not None:
                active_modules.append("Sediment")
            if hasattr(self.nml, "wwminput") and self.nml.wwminput is not None:
                active_modules.append("Wave")

            if active_modules:
                logger.info(f"{ARROW} Active modules: {', '.join(active_modules)}")

            # Update times and write namelists
            self.nml.update_times(period=runtime.period)
            self.nml.write_nml(runtime.staging_dir)
            logger.info(f"{ARROW} Namelists configured successfully")

        return str(runtime.staging_dir)

    def _format_value(self, obj):
        """Custom formatter for SCHISMConfig values.

        This method provides special formatting for specific types used in
        SCHISMConfig such as grid, data, and namelist components.

        Args:
            obj: The object to format

        Returns:
            A formatted string or None to use default formatting
        """
        # Import specific types and formatting utilities
        from rompy.formatting import get_formatted_header_footer
        from rompy.logging import LoggingConfig

        # Get ASCII mode setting from LoggingConfig
        logging_config = LoggingConfig()
        USE_ASCII_ONLY = logging_config.use_ascii

        # Format SCHISMConfig (self-formatting)
        if isinstance(obj, SCHISMConfig):
            header, footer, bullet = get_formatted_header_footer(
                title="SCHISM MODEL CONFIGURATION", use_ascii=USE_ASCII_ONLY
            )

            lines = [header]

            # Add grid information
            if obj.grid is not None:
                grid_type = type(obj.grid).__name__
                lines.append(f"  {bullet} Grid: {grid_type}")

                # Try to get grid details
                if hasattr(obj.grid, "hgrid"):
                    hgrid = obj.grid.hgrid
                    hgrid_path = str(
                        getattr(hgrid, "uri", getattr(hgrid, "source", hgrid))
                    )
                    if len(hgrid_path) > 50:
                        hgrid_path = "..." + hgrid_path[-47:]
                    lines.append(f"      Horizontal grid: {hgrid_path}")

                if hasattr(obj.grid, "vgrid") and obj.grid.vgrid is not None:
                    vgrid_type = type(obj.grid.vgrid).__name__
                    lines.append(f"      Vertical grid: {vgrid_type}")

            # Add data information
            if obj.data is not None:
                data_type = type(obj.data).__name__
                lines.append(f"  {bullet} Data: {data_type}")

                # Count data components
                data_components = []
                if hasattr(obj.data, "atmos") and obj.data.atmos is not None:
                    data_components.append("Atmospheric")
                if hasattr(obj.data, "wave") and obj.data.wave is not None:
                    data_components.append("Wave")
                if (
                    hasattr(obj.data, "boundary_conditions")
                    and obj.data.boundary_conditions is not None
                ):
                    data_components.append("Boundary Conditions")

                if data_components:
                    lines.append(f"      Components: {', '.join(data_components)}")

            # Add namelist information
            if obj.nml is not None:
                nml_type = type(obj.nml).__name__
                lines.append(f"  {bullet} Namelist: {nml_type}")

                # Count active namelist components
                nml_components = []
                if hasattr(obj.nml, "param") and obj.nml.param is not None:
                    nml_components.append("Parameters")
                if hasattr(obj.nml, "ice") and obj.nml.ice is not None:
                    nml_components.append("Ice")
                if hasattr(obj.nml, "icm") and obj.nml.icm is not None:
                    nml_components.append("ICM")
                if hasattr(obj.nml, "sediment") and obj.nml.sediment is not None:
                    nml_components.append("Sediment")
                if hasattr(obj.nml, "wwminput") and obj.nml.wwminput is not None:
                    nml_components.append("Wave")

                if nml_components:
                    lines.append(f"      Active modules: {', '.join(nml_components)}")

            # Add template information
            if obj.template is not None:
                template_path = obj.template
                if len(template_path) > 50:
                    template_path = "..." + template_path[-47:]
                lines.append(f"  {bullet} Template: {template_path}")

            lines.append(footer)
            return "\n".join(lines)

        # Format SCHISMGrid
        from .grid import SCHISMGrid

        if isinstance(obj, SCHISMGrid):
            header, footer, bullet = get_formatted_header_footer(
                title="SCHISM GRID", use_ascii=USE_ASCII_ONLY
            )

            lines = [header]

            if hasattr(obj, "hgrid"):
                hgrid = obj.hgrid
                hgrid_path = str(getattr(hgrid, "uri", getattr(hgrid, "source", hgrid)))
                if len(hgrid_path) > 50:
                    hgrid_path = "..." + hgrid_path[-47:]
                lines.append(f"  {bullet} Horizontal Grid: {hgrid_path}")

            if hasattr(obj, "vgrid") and obj.vgrid is not None:
                vgrid_type = type(obj.vgrid).__name__
                lines.append(f"  {bullet} Vertical Grid: {vgrid_type}")

                # Try to get vertical grid details
                if hasattr(obj.vgrid, "nlayer") and hasattr(obj.vgrid, "nlayer"):
                    nlayer = getattr(obj.vgrid, "nlayer", None)
                    if nlayer is not None:
                        lines.append(f"      Layers: {nlayer}")

            lines.append(footer)
            return "\n".join(lines)

        # Format SCHISMData
        from .data import SCHISMData

        if isinstance(obj, SCHISMData):
            header, footer, bullet = get_formatted_header_footer(
                title="SCHISM DATA", use_ascii=USE_ASCII_ONLY
            )

            lines = [header]

            # Count and list data components
            components = {}
            if hasattr(obj, "atmos") and obj.atmos is not None:
                components["Atmospheric"] = type(obj.atmos).__name__
            if hasattr(obj, "wave") and obj.wave is not None:
                components["Wave"] = type(obj.wave).__name__
            if (
                hasattr(obj, "boundary_conditions")
                and obj.boundary_conditions is not None
            ):
                components["Boundary Conditions"] = type(
                    obj.boundary_conditions
                ).__name__

            for comp_name, comp_type in components.items():
                lines.append(f"  {bullet} {comp_name}: {comp_type}")

            if not components:
                lines.append(f"  {bullet} No data components configured")

            lines.append(footer)
            return "\n".join(lines)

        # Format NML
        from .namelists import NML

        if isinstance(obj, NML):
            header, footer, bullet = get_formatted_header_footer(
                title="SCHISM NAMELIST", use_ascii=USE_ASCII_ONLY
            )

            lines = [header]

            # List active namelist components
            components = {}
            if hasattr(obj, "param") and obj.param is not None:
                components["Parameters"] = type(obj.param).__name__
            if hasattr(obj, "ice") and obj.ice is not None:
                components["Ice"] = type(obj.ice).__name__
            if hasattr(obj, "icm") and obj.icm is not None:
                components["ICM"] = type(obj.icm).__name__
            if hasattr(obj, "sediment") and obj.sediment is not None:
                components["Sediment"] = type(obj.sediment).__name__
            if hasattr(obj, "wwminput") and obj.wwminput is not None:
                components["Wave"] = type(obj.wwminput).__name__
            if hasattr(obj, "cosine") and obj.cosine is not None:
                components["CoSiNE"] = type(obj.cosine).__name__
            if hasattr(obj, "mice") and obj.mice is not None:
                components["MICE"] = type(obj.mice).__name__

            for comp_name, comp_type in components.items():
                lines.append(f"  {bullet} {comp_name}: {comp_type}")

            if not components:
                lines.append(f"  {bullet} No modules configured")

            lines.append(footer)
            return "\n".join(lines)

        # Use the new formatting framework
        from rompy.formatting import format_value

        return format_value(obj)

Attributes

model_type class-attribute instance-attribute

model_type: Literal['schism'] = Field('schism', description='The model type for SCHISM.')

grid class-attribute instance-attribute

grid: SCHISMGrid = Field(description='The model grid')

data class-attribute instance-attribute

data: Optional[SCHISMData] = Field(None, description='Model inputs')

nml class-attribute instance-attribute

nml: Optional[NML] = Field(default_factory=lambda: NML(param=Param()), description='The namelist')

template class-attribute instance-attribute

template: Optional[str] = Field(description='The path to the model template', default=SCHISM_TEMPLATE)

model_config class-attribute instance-attribute

model_config = ConfigDict(arbitrary_types_allowed=True, from_attributes=True)

plot_sflux_spatial class-attribute instance-attribute

plot_sflux_spatial = plot_sflux_spatial

plot_sflux_timeseries class-attribute instance-attribute

plot_sflux_timeseries = plot_sflux_timeseries

plot_boundary_points class-attribute instance-attribute

plot_boundary_points = plot_boundary_points

plot_boundary_timeseries class-attribute instance-attribute

plot_boundary_timeseries = plot_boundary_timeseries

plot_boundary_profile class-attribute instance-attribute

plot_boundary_profile = plot_boundary_profile

plot_tidal_boundaries class-attribute instance-attribute

plot_tidal_boundaries = plot_tidal_boundaries

plot_tidal_stations class-attribute instance-attribute

plot_tidal_stations = plot_tidal_stations

plot_tidal_rose class-attribute instance-attribute

plot_tidal_rose = plot_tidal_rose

plot_tidal_dataset class-attribute instance-attribute

plot_tidal_dataset = plot_tidal_dataset

Functions

check_hotstart

check_hotstart()
Source code in rompy_schism/config.py
@model_validator(mode="after")
def check_hotstart(self):
    if (
        self.data is not None
        and hasattr(self.data, "hotstart")
        and getattr(self.data, "hotstart", None) is not None
        and self.nml is not None
        and self.nml.param is not None
        and self.nml.param.opt is not None
    ):
        self.nml.param.opt.ihot = 1
    return self

serialize_model

serialize_model(**kwargs)

Custom serializer to handle proper serialization of nested components.

Source code in rompy_schism/config.py
@model_serializer
def serialize_model(self, **kwargs):
    """Custom serializer to handle proper serialization of nested components."""
    from rompy_schism.grid import GR3Generator

    result = {}

    # Explicitly handle required fields
    result["model_type"] = self.model_type

    # Handle grid separately to process GR3Generator objects
    if self.grid is not None:
        grid_dict = {}
        for field_name in self.grid.model_fields:
            value = getattr(self.grid, field_name, None)

            # Special handling for GR3Generator objects
            if value is not None and isinstance(value, GR3Generator):
                # For GR3Generator objects, extract just the value field
                grid_dict[field_name] = value.value
            elif value is not None and not field_name.startswith("_"):
                grid_dict[field_name] = value

        result["grid"] = grid_dict

    # Add optional fields that are not None
    if self.data is not None:
        result["data"] = self.data

    if self.nml is not None:
        result["nml"] = self.nml

    if self.template is not None:
        result["template"] = self.template

    return result