Skip to content

Semi-distributed XAJ (semi_xaj)

semi_xaj is a semi-distributed version of the XinAnJiang model, which applies the XAJ runoff-generation scheme at sub-basin scale before routing.

  • Registered name: semi_xaj
  • Routing: Muskingum-style sub-basin routing

API Reference

Copyright (c) 2023-2026 Wenyu Ouyang. All rights reserved.

semi_xaj(p_and_e, attributes, modelwithsameParas, para_seq, params_range, topo, dt, normalized_params='auto', **kwargs)

Semi-distributed XAJ model implementation with topology support.

Parameters

p_and_e : ndarray Precipitation and evaporation data attributes : object Basin attributes data modelwithsameParas : list Model parameter configuration para_seq : ndarray Parameter sequence array params_range : list Parameter range definitions topo : list Topology configuration dt : float Time step normalized_params : Union[bool, str], optional Parameter format specification (maintained for compatibility): - "auto": Automatically detect parameter format (default) - True: Parameters are normalized (0-1 range), convert to original scale - False: Parameters are already in original scale, use as-is Note: This model uses custom parameter processing logic **kwargs : dict Additional keyword arguments

Returns

ndarray Simulated streamflow

Source code in hydromodel/models/semi_xaj.py
def semi_xaj(
    p_and_e,
    attributes,
    modelwithsameParas,
    para_seq,
    params_range,
    topo,
    dt,
    normalized_params="auto",
    **kwargs,
):
    """
    Semi-distributed XAJ model implementation with topology support.

    Parameters
    ----------
    p_and_e : ndarray
        Precipitation and evaporation data
    attributes : object
        Basin attributes data
    modelwithsameParas : list
        Model parameter configuration
    para_seq : ndarray
        Parameter sequence array
    params_range : list
        Parameter range definitions
    topo : list
        Topology configuration
    dt : float
        Time step
    normalized_params : Union[bool, str], optional
        Parameter format specification (maintained for compatibility):
        - "auto": Automatically detect parameter format (default)
        - True: Parameters are normalized (0-1 range), convert to original scale
        - False: Parameters are already in original scale, use as-is
        Note: This model uses custom parameter processing logic
    **kwargs : dict
        Additional keyword arguments

    Returns
    -------
    ndarray
        Simulated streamflow
    """
    model_name = kwargs.get("name", "xaj")
    source_type = kwargs.get("source_type", "sources")
    source_book = kwargs.get("source_book", "HF")
    qsim_collect = np.zeros((len(p_and_e), len(topo), 1))
    print(para_seq.shape, "------------------------------------")

    # 把长序列参数分配给各个ParaID
    start_index = 0
    for item in modelwithsameParas:
        param_length = len(item["PARAMETER"])
        item["PARAMETER"] = para_seq[
            start_index : start_index + param_length
        ].tolist()
        start_index += param_length

    for i in range(len(modelwithsameParas)):
        modelIdSet = modelwithsameParas[i]["MODELIDSET"]
        for j in range(len(modelIdSet)):
            params_range[modelIdSet[j] - 1]["PARAMETER"] = modelwithsameParas[
                i
            ]["PARAMETER"]
            params_range[modelIdSet[j] - 1]["UP"] = modelwithsameParas[i]["UP"]
            params_range[modelIdSet[j] - 1]["DOWN"] = modelwithsameParas[i][
                "DOWN"
            ]

    lineN0 = 0
    for calid in topo:
        topovalue = calid.split()
        numbers = np.array([int(num) for num in topovalue])  # 每一行的拓扑值
        p_and_e1 = p_and_e[:, lineN0 : lineN0 + 1, :]
        lineN0 = lineN0 + 1

        for i in range(len(numbers)):
            start, end = numbers[i], numbers[0]
            modelid = [
                model["MODELID"]
                for model in params_range
                if model["START"] == start and model["END"] == end
            ]
            modelname = params_range[modelid[0] - 1]["MODELNAME"]

            if modelname == "XAJ":
                print(f"Running XAJ")
                parameter = np.array(params_range[modelid[0] - 1]["PARAMETER"])
                parameterup = np.array(params_range[modelid[0] - 1]["UP"])
                parameterdown = np.array(params_range[modelid[0] - 1]["DOWN"])

                # Use existing parameter processing logic for semi_xaj model
                # This maintains compatibility with the existing semi_xaj parameter format
                parameter_xaj = (
                    parameterup - parameterdown
                ) * parameter + parameterdown
                parameter_xaj = parameter_xaj.reshape(-1, 1)
                print(attributes, "wwwwwwwwwwwwwwww")
                area = attributes.sel(id=str(numbers[0]))["area"].values
                qsim, _ = xaj(
                    p_and_e1,
                    params=parameter_xaj,
                    warmup_length=0,
                    model_name=model_name,
                    source_type=source_type,
                    source_book=source_book,
                    time_interval_hours=dt,
                )
                qsim = qsim.squeeze() * area / (3600 * dt * 1000 / 1000000)
                qsim_collect[:, numbers[0] - 1, 0] += qsim
                print(
                    f"node:{start}-{end}\tmodel:{modelname}\tarea:{area}\tparameter:{parameter_xaj.squeeze()}"
                )

            elif modelname == "MUSK":
                print(f"Running MUSK")
                parameter = np.array(params_range[modelid[0] - 1]["PARAMETER"])
                inflows = qsim_collect[:, start - 1, 0]
                outflows = Musk(inflows, parameter[0], parameter[1], dt=dt)
                qsim_collect[:, end - 1, 0] += outflows
    return qsim_collect