Skip to content

Documentation

Bases: FM2ProfBase

Visualise output class.

Instantiate a VisualiseOutput object.

Source code in fm2prof\utils.py
def __init__(
    self,
    output_directory: str,
    logger: Logger | None = None,
) -> None:
    """Instantiate a VisualiseOutput object."""
    super().__init__(logger=logger)

    if not logger:
        self._create_logger()
    self.output_dir = Path(output_directory)
    self.fig_dir = self._generate_output_dir()
    self._set_files()
    self._ref_geom_y = []
    self._ref_geom_tw = []
    self._ref_geom_fw = []

    self.set_logger_message(f"Using {self.fig_dir} as output directory for figures")

    # initiate plotstyle
    PlotStyles.apply()

branches: tuple[np.ndarray, np.ndarray] property

Get branches.

cross_sections: Generator[dict, None, None] property

Generator to loop through all cross-sections in definition file.

Example use:

for css in visualiser.cross_sections: visualiser.make_figure(css)

number_of_cross_sections: int property

Get number of cross sections.

figure_cross_section(css, reference_geometry=(), reference_roughness=(), *, save_to_file=True, overwrite=False)

Get a figure of the cross section.

Parameters:

Name Type Description Default
css dict

cross section dict

required
reference_geometry tuple

tuple of reference . Defaults to ().

()
reference_roughness tuple

description. Defaults to ().

()
save_to_file bool

Save the figure to file. Defaults to True.

True
overwrite bool

Overwrite the figure. Defaults to False.

False

Returns:

Name Type Description
Figure Figure

description

Source code in fm2prof\utils.py
def figure_cross_section(
    self,
    css: dict,
    reference_geometry: tuple = (),
    reference_roughness: tuple = (),
    *,
    save_to_file: bool = True,
    overwrite: bool = False,
) -> Figure:
    """Get a figure of the cross section.

    Args:
        css (dict): cross section dict
        reference_geometry (tuple, optional): tuple of reference . Defaults to ().
        reference_roughness (tuple, optional): _description_. Defaults to ().
        save_to_file (bool, optional): Save the figure to file. Defaults to True.
        overwrite (bool, optional): Overwrite the figure. Defaults to False.

    Returns:
        Figure: _description_

    """
    output_dir = self.fig_dir.joinpath("cross_sections")
    output_dir.mkdir(exist_ok=True)

    output_file = output_dir.joinpath(f"{css['id']}.png")
    if output_file.is_file() and not overwrite:
        self.set_logger_message("file already exists", "debug")
        return None
    try:
        fig = plt.figure(figsize=(8, 12))
        gs = fig.add_gridspec(2, 2)
        axs = [
            fig.add_subplot(gs[0, :]),
            fig.add_subplot(gs[1, 0]),
            fig.add_subplot(gs[1, 1]),
        ]

        self._plot_geometry(css, axs[0], reference_geometry)
        self._plot_volume(css, axs[1])
        self._plot_roughness(css, axs[2], reference_roughness)

        fig, lgd = self._set_plot_style(fig)

        if save_to_file:
            plt.savefig(
                output_file,
                bbox_extra_artists=[lgd],
                bbox_inches="tight",
            )
        else:
            return fig

    except Exception as e:
        self.set_logger_message(f"error processing: {css['id']} {e!s}", "error")
        return None

    finally:
        plt.close()

figure_roughness_longitudinal(branch)

Get figure of longitudinal roughness.

Assumes the following naming convention: [branch][optional:branch_order][chainage]

Source code in fm2prof\utils.py
def figure_roughness_longitudinal(self, branch: str) -> None:
    """Get figure of longitudinal roughness.

    Assumes the following naming convention:
    [branch]_[optional:branch_order]_[chainage]
    """
    output_dir = self.fig_dir.joinpath("roughness")
    output_dir.mkdir(exist_ok=True)

    fig, ax = plt.subplots(1, figsize=(12, 5))

    css = self.get_cross_sections_for_branch(branch)

    chainage = []
    minmax = []
    for cross_section in css:
        chainage.append(cross_section[1])
        roughness = self.get_roughness_info_for_css(cross_section[0])[1]
        minmax.append([min(roughness), max(roughness)])

    chainage = np.array(chainage) * 1e-3
    minmax = np.array(minmax)
    ax.plot(chainage, minmax[:, 0], label="minimum")
    ax.plot(chainage, minmax[:, 1], label="maximum")
    ax.set_ylabel("Ruwheid (Chezy)")
    ax.set_xlabel("Afstand [km]")
    ax.set_title(branch)
    fig, lgd = self._set_plot_style(fig, use_legend=True)
    plt.savefig(
        output_dir.joinpath(f"roughness_longitudinal_{branch}.png"),
        bbox_extra_artists=[lgd],
        bbox_inches="tight",
    )

get_cross_section_by_id(css_id)

Get cross-section information given an id.

Parameters:

Name Type Description Default
css_id str

cross-section name

required
Source code in fm2prof\utils.py
def get_cross_section_by_id(self, css_id: str) -> dict | None:
    """Get cross-section information given an id.

    Args:
        css_id (str): cross-section name

    """
    csslist = self._read_css_def_file()
    for css in csslist:
        if css.get("id") == css_id:
            return css
    return None

get_cross_sections_for_branch(branch)

Get cross sections for branch name.

Parameters:

Name Type Description Default
branch str

branch name.

required

Returns:

Type Description
tuple[str, float, str]

tuple[str, float, str]:

Source code in fm2prof\utils.py
def get_cross_sections_for_branch(self, branch: str) -> tuple[str, float, str]:
    """Get cross sections for branch name.

    Args:
        branch (str): branch name.


    Returns:
        tuple[str, float, str]:

    """
    if branch not in self.branches:
        err_msg = f"Branch {branch} not in known branches: {self.branches}"
        raise KeyError(err_msg)

    css_list = [self.split_css(css.get("id")) for css in self.cross_sections]
    branches, contiguous_branches = self.branches
    branch_list = []
    sub_branches = np.unique([b for b in branches if b.startswith(branch)])
    running_chainage = 0
    for i, sub_branch in enumerate(sub_branches):
        sublist = self.get_css_for_branch(css_list, sub_branch)
        if i > 0:
            running_chainage += self.get_css_for_branch(css_list, sub_branches[i - 1])[-1][1]
        branch_list.extend([(s[0], s[1] + running_chainage, s[2]) for s in sublist])

    return branch_list

get_css_for_branch(css_list, branchname) staticmethod

Get cross section for given branch name.

Source code in fm2prof\utils.py
@staticmethod
def get_css_for_branch(css_list: list[tuple[str, float, str]], branchname: str) -> list[tuple[str, float, str]]:
    """Get cross section for given branch name."""
    return [c for c in css_list if c[2].startswith(branchname)]

get_roughness_info_for_css(cssname, rtype='roughnessMain')

Open roughness file and reads information for a given cross-section name.

Source code in fm2prof\utils.py
def get_roughness_info_for_css(self, cssname: str, rtype: str = "roughnessMain") -> tuple[list, list]:
    """Open roughness file and reads information for a given cross-section name."""
    levels = None
    values = None
    with self.files[rtype].open("r") as f:
        cssbranch, csschainage = self._parse_cssname(cssname)
        for line in f:
            if line.strip().lower() == "[branchproperties]" and self._get_value_from_line(f).lower() == cssbranch:
                [f.readline() for i in range(3)]
                levels = list(map(float, self._get_value_from_line(f).split()))
            if (
                line.strip().lower() == "[definition]"
                and self._get_value_from_line(f).lower() == cssbranch
                and float(self._get_value_from_line(f).lower()) == csschainage
            ):
                values = list(map(float, self._get_value_from_line(f).split()))
    return levels, values

get_volume_info_for_css(cssname)

Get volume info for cross section.

Source code in fm2prof\utils.py
def get_volume_info_for_css(self, cssname: str) -> dict:
    """Get volume info for cross section."""
    column_names = [
        "z",
        "2D_total_volume",
        "2D_flow_volume",
        "2D_wet_area",
        "2D_flow_area",
        "1D_total_volume_sd",
        "1D_total_volume",
        "1D_flow_volume_sd",
        "1D_flow_volume",
        "1D_total_width",
        "1D_flow_width",
    ]
    cssdata = {}
    for column in column_names:
        cssdata[column] = []

    with self.files["volumes"].open("r") as f:
        for line in f:
            values = line.strip().split(",")
            if values[0] == cssname:
                for i, column in enumerate(column_names):
                    cssdata[column].append(float(values[i + 1]))

    return cssdata

parse_cross_section_definition_file(css_def) staticmethod

Parse cross-section definition file.

Parameters:

Name Type Description Default
css_def Path

path to cross-section definition file

required

Returns: list[CrossSectionDefinition]: list of cross-section definitions

Source code in fm2prof\utils.py
@staticmethod
def parse_cross_section_definition_file(css_def: Path) -> list[CrossSectionDefinition]:
    """Parse cross-section definition file.

    Args:
        css_def (Path): path to cross-section definition file
    Returns:
        list[CrossSectionDefinition]: list of cross-section definitions
    """
    csslist = []
    with css_def.open("r") as f:
        for line in f:
            if line.lower().strip() == "[definition]":
                css_id = f.readline().strip().split("=")[1]
                [f.readline() for i in range(3)]
                css_levels = list(map(float, VisualiseOutput._get_value_from_line(f).split()))
                css_fwidth = list(map(float, VisualiseOutput._get_value_from_line(f).split()))
                css_twidth = list(map(float, VisualiseOutput._get_value_from_line(f).split()))
                css_sdcrest = float(VisualiseOutput._get_value_from_line(f))
                css_sdflow = float(VisualiseOutput._get_value_from_line(f))
                css_sdtotal = float(VisualiseOutput._get_value_from_line(f))
                css_sdbaselevel = float(VisualiseOutput._get_value_from_line(f))
                css_mainsectionwidth = float(VisualiseOutput._get_value_from_line(f))
                css_fp1sectionwidth = float(VisualiseOutput._get_value_from_line(f))

                css: CrossSectionDefinition = CrossSectionDefinition(
                    id=css_id.strip(),
                    levels=css_levels,
                    flow_width=css_fwidth,
                    total_width=css_twidth,
                    SD_crest=css_sdcrest,
                    SD_flow_area=css_sdflow,
                    SD_total_area=css_sdtotal,
                    SD_baselevel=css_sdbaselevel,
                    mainsectionwidth=css_mainsectionwidth,
                    fp1sectionwidth=css_fp1sectionwidth,
                )

                csslist.append(css)

    return csslist

plot_cross_sections()

Plot figures for all cross-sections in project.

Outputs to output directory of project.

Source code in fm2prof\utils.py
def plot_cross_sections(self) -> None:
    """Plot figures for all cross-sections in project.

    Outputs to output directory of project.
    """
    pbar = tqdm.tqdm(total=self.number_of_cross_sections)
    self.start_new_log_task("Plotting cross-secton figures", pbar=pbar)

    for css in self.cross_sections:
        self.figure_cross_section(css)
        pbar.update(1)

    self.finish_log_task()

split_css(name) staticmethod

Split cross section name.

Source code in fm2prof\utils.py
@staticmethod
def split_css(name: str) -> tuple[str, float, str]:
    """Split cross section name."""
    chainage = float(name.split("_")[-1])
    branch = "_".join(name.split("_")[:-1])
    return (name, chainage, branch)