Skip to content

problem with heads in streamflow routing #2542

@eacuellarq

Description

@eacuellarq

Hello,

I have been having some troubles the heads in modflow thus I got this profiles in my river always and I cannot understand why the line is transform to curve. Here I attached the code and image with results

Image

`
import os
import flopy
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.gridspec as gridspec
from utils.inputs import (generate_topo,
generate_river_stress_period_data,
plot_sfr_results, hydrograph_cooper_rorabaugh, plot_results_hds_cbc, extract_sfr_data, calculate_tsmult,
analyze_model_timing)
import phi_numbers2 as phinum
import pandas as pd
from flopy.utils.sfroutputfile import SfrFile

PARÁMETROS DEL MODELO. Everything in SI system (m, s, m3/s, m2, etc)

dt = pd.read_csv('sample_points.csv')
for n in range(50, 55, 5):

# Asignación directa desde dt (solo las columnas indicadas)
d_bkf     = dt["d_bkf"][n]*2      # Profundidad del banco filtrante [m]
w_bkf     = dt["w_bkf"][n]      # Ancho del banco filtrante [m]
L         = dt["L"][n]           # Longitud del dominio [m]
B         = dt["B"][n]          # Ancho del dominio [m]
D         = dt["D"][n]*2           # Profundidad activa del acuífero [m]
Ka        = dt["Ka"][n]          # Conductividad hidráulica horizontal [m/s]
Kr        = dt["Kr"][n]          # Conductividad hidráulica del lecho [m/s]
dr        = dt["dr"][n]          # Espesor de la zona (dr) [m]
Sy        = dt["Sy"][n]          # Porosidad efectiva [-]
Slope         = dt["S"][n]
Ss        = 1e-7          # Compresibilidad específica [1/m]

n_manning    = dt["n"][n]           # Rugosidad de Manning [-]
H_0       = dt["H_0"][n]         # Nivel base inicial [m]
t_d_days  = dt["t_d_days"][n]    # Duración del evento [d]

deltay = Slope*L
Slope_trans = 1e-20
Kv_Kh = 1 # Kv/Kh Anisotropia en el ac

# Hydrograph
H_0 = H_0    # Nivel base (m)
eta_0 = (d_bkf-H_0)/H_0 # (d_bkf - H_0)/H_0   # H_p = 1*H_0 = 2m # coeffient, max stage of water. Evita que la creciente no sobrepase la banca llena.
t_d = 1*24*3600    # Duración del evento (s)
eta_d = 0.5 #Asimetria del hidrog. Valores peq. representan crecidas rapidas que bajan lento. explorar valores entre
H_p= eta_0 * H_0  #Elevanción máxima durante la creciente

nrow=int(dt["nrow"][n])
ncol=30
# nrow=15
# ncol=11
delc = L/nrow
delr = B/ncol

nlay = 5 # numero de capas

# Crear el modelo básico
path_results = "../results/simple_model/"
name_model = 'water_sim_bagre'
model = flopy.modflow.Modflow(name_model,
                            exe_name="MODFLOW-NWT.exe",
                            version="mfnwt",
                            model_ws=path_results,
                            verbose=True
                            )

hydrom_times = 600
t = np.arange(0, t_d, hydrom_times) # tmin, tmax, t_discretize
T = Ka * d_bkf  # Transmisividad del acuífero

# Visualizar hidrograma
H_b = hydrograph_cooper_rorabaugh(t, H_0, eta_0, eta_d, t_d)
Q = (1/n_manning) * (H_b**(5/3)) * (Slope**(1/2)) * (w_bkf) # m3/s
idx_Qmax = np.argmax(Q)

velocity = Q / (w_bkf * d_bkf)
T_arrive = (L/velocity)/(3600*24)

v_mean = Q.mean()/(w_bkf*d_bkf)
v_max =  Q.max()/(w_bkf*d_bkf)

t_mean = L/v_mean 
t_max = L/v_max
t_canal = L / v_max


tc = phinum.get_tc(Sy=Sy, Ka=Ka, B=B,  d_bkf=d_bkf)

a_celda = np.sqrt(delr * delc) # Dimensión de celda representativa
tc3 = Sy * a_celda**2 / (4 * T) # Tiempo característico tc2  tpo de difusión de la perturbación hidrúalica en el modelo.
t_pico = t[idx_Qmax] # Tiempo al pico del evento
# valores = np.array([t_canal, t_lecho, tc,t_pico, tc2]) # Agrupar todos los tiempos para análisis
valores = 1/np.array([t_canal, tc,t_pico, tc3])

TC = len(valores) / np.sum(valores) # Calcular TC como media armónica (pondera procesos rápidos)
delta_t_TC = 0.25 * TC  # s # Calcular delta_t con factor de seguridad
reach_len = L / nrow # Evaluar delta_t que cumple Courant
delta_t_courant = .85*reach_len / v_max  # Co = 1
delta_t = min(delta_t_TC, delta_t_courant) # Elegir el menor de ambos para respetar procesos rápidos y condición de estabilidad
courant_final = v_max * delta_t / reach_len # Calcular Courant final (referencia)
duracion_transitorio = 20 * 24 * 3600  # segundos # Definir duración total del periodo transitorio (ajustable más adelante)

# Número de pasos para el periodo transitorio
nstp_trans = int(np.ceil(duracion_transitorio / delta_t))

perlen = [1*24*3600, duracion_transitorio]  # periodo estacionario y transitorio
nstp = [1, 400]


# Crear Topografía y caja del modelo
topo = generate_topo(nrow, ncol,L=L, delta_h=deltay, slope_t=Slope_trans, min_topo=D+d_bkf+1, plot=True)
botm = np.linspace(D, 0, nlay)

print(nstp)

# perlen = [.001*24*3600, 10*24*3600] # Periodos-longitud temporal
nper = perlen.__len__() # Numero de periodos
# nstp = [1,6000] # step per period
steady = [True, False] # Tipo de periodo
# cte = calculate_tsmult(perlen[1]/24/3600, nstp[1], 500, time_refine=(t_d/24/3600)*2)
# tsmult = [1, cte]
dis = flopy.modflow.ModflowDis(
    model,
    nlay=nlay,  # Número de capas
    nrow=nrow, # numero de columnas
    ncol=ncol, # numero de filas
    delr=delr,#esh_custom_unit.flatten(), # tamaño dy o entre filas
    delc=delc, # tamaño dx o entre columnas
    top=topo, # topografia o capa top
    botm=botm, # Capas inferiores (nlay, nrow, ncol)
    itmuni=1, # time unit 0-undefined, 1-seconds, 2-minutes, 3-hours, 4-days, 5-years
    lenuni=2, # length unit 0-undefined, 1-feet, 2-meters, 3-centimeters
    nper= nper,
    perlen=perlen,
    nstp=nstp,
    steady=steady,
    # tsmult=tsmult
)

# dt1 = perlen[1] * (tsmult[1] - 1) / (tsmult[1]**nstp[1] - 1)
# time_steps = np.array([dt1 * tsmult[1]**i for i in range(nstp[1])])
# cumulative_time = np.cumsum(time_steps)

#Entrada del hidrograma o perturbación al sistema
t0 = perlen[0]
t_delay = 1*24*3600
hydrogram = np.c_[t + t0 + t_delay, np.vstack(Q)] # Hydrogram by rating curve
np.savetxt(os.path.join(path_results, "QHidrom.dat"), hydrogram, fmt="%.4f")

# Save in memory place Modflow
model.add_external(
    fname='QHidrom.dat',  # name file
    unit=70,                    # unit logic
)

# Dictionary for segment
tabfiles_dict = {
    1: {                    # segment number
        'numval': len(hydrogram),      # len Qs
        'inuit': 70         # Unit logic Hydrogram
    }
}

#River definition
river_params_basic = {
    'A': 0,
    'B': 0.5,
    'C': 0,
    'start_cell': int(ncol/2),
    'orientation': "v",
    'width_riv': 1,
    'plot': True
}


stage_per_time = [.5]

orientation=river_params_basic["orientation"]

all_river = generate_river_stress_period_data(nrow=nrow, ncol=ncol, topo=topo, nper=nper, stage_per_time=stage_per_time,
                                del_rbot=5, river_params=river_params_basic, type_gen='basic')
i_riv = all_river["i"]
j_riv = all_river["j"]
river=all_river["river"]
nc_riv = all_river["nc_river"]

# Añadir UPW (Upstream Weighting Package)
upw = flopy.modflow.ModflowUpw(
    model,
    hk=nlay*[Ka],         # Conductividad hidráulica horizontal.(m/d) trabajar entre 5 y 50.
    vka=nlay*[Kv_Kh*Ka],         # Conductividad hidráulica vertical. (m/d) .  trabajar entre 0.1 y 0.3
    sy=nlay*[Sy],         # Rendimiento específico (acuíferos no confinados) sy de 0,15a 0,35 si es más gravoso
    ss=nlay*[Ss],         # Almacenamiento específico (acuíferos confinados) MF lo exige incluso en libres. valor que sugieren poner 1e-7 en caso de ac. libre..
    laytyp=nlay*[1],      # 1 = No confinado, 0 = Confinado. El valor de 1 habilita el drenaje gravitacional.
    hdry=-1e30,      # Valor de referencia para celdas secas
    ipakcb=53,       # Unidad de salida para balance de agua
    iphdry=0         # Flag para celdas secas (opcional)
)

# Parámetros del solver
nwt = flopy.modflow.ModflowNwt(model, iprnwt=1, maxiterout=20000, fluxtol=1000, headtol=1e-6)

#Reach parametrization
reach_data = flopy.modflow.ModflowSfr2.get_empty_reach_data(nreaches=len(i_riv))
reach_data["k"] = 0  # Capa
reach_data["i"] = i_riv  # Filas
reach_data["j"] = j_riv  # Columna fija para un río recto
reach_data["iseg"] = 1
reach_data["rchlen"] = delc  # Longitud del reach
reach_data["strtop"] = topo[i_riv, j_riv]-d_bkf#topo[i_riv, j_riv]-d_bkf#topo[i_riv, j_riv]-4  # Elevación // ISFROPT > 0
reach_data["ireach"] = list(range(1, nc_riv+1))
reach_data["slope"] = 0  # Pendiente ISFROPT > 0
reach_data["strthick"] = 0  # Espesor del lecho ISFROPT > 0
reach_data["strhc1"] = 0 # Conductividad hidráulica del lecho ISFROPT > 0

#Segment definition
segment_data_steady = flopy.modflow.ModflowSfr2.get_empty_segment_data(1)
segment_data_steady[0]["nseg"] = 1       # Número del segmento
segment_data_steady[0]["icalc"] = 0      # Ecuación de Manning
segment_data_steady[0]["outseg"] = 0     # Segmento terminal
segment_data_steady[0]["flow"] =  0 # Flujo inicial en m³/s
segment_data_steady[0]["elevup"] = topo[i_riv, j_riv][0] - d_bkf # Elevación del lecho del rio aguas arriba
segment_data_steady[0]["elevdn"] = topo[i_riv, j_riv][-1] - d_bkf # Elevación del lecho del rio  aguas abajo
segment_data_steady[0]["hcond1"] = Kr  # Conductividad hidráulica aguas arriba
segment_data_steady[0]["hcond2"] = Kr  # Conductividad hidráulica aguas abajo
segment_data_steady[0]["thickm1"] = dr #1  # Espesor del lecho aguas arriba
segment_data_steady[0]["thickm2"] = dr #1  # Espesor del lecho aguas abajo
segment_data_steady[0]["roughch"] = n_manning #0.1
segment_data_steady[0]["roughbk"] = 0#0.005
segment_data_steady[0]["width1"] = w_bkf#1
segment_data_steady[0]["width2"] = w_bkf#10
segment_data_steady[0]["depth1"] = H_0#1
segment_data_steady[0]["depth2"] = H_0#10

segment_data_transient = flopy.modflow.ModflowSfr2.get_empty_segment_data(1)
segment_data_transient[0]["nseg"] = 1       # Número del segmento
segment_data_transient[0]["icalc"] = 1      # Ecuación de Manning
segment_data_transient[0]["outseg"] = 0     # Segmento terminal
segment_data_transient[0]["flow"] =  Q.min() # Flujo inicial en m³/s
segment_data_transient[0]["elevup"] = topo[i_riv, j_riv][0] - d_bkf # Elevación del lecho del rio aguas arriba
segment_data_transient[0]["elevdn"] = topo[i_riv, j_riv][-1] - d_bkf # Elevación del lecho del rio  aguas abajo
segment_data_transient[0]["hcond1"] = Kr  # Conductividad hidráulica aguas arriba
segment_data_transient[0]["hcond2"] = Kr  # Conductividad hidráulica aguas abajo
segment_data_transient[0]["thickm1"] = dr #1  # Espesor del lecho aguas arriba
segment_data_transient[0]["thickm2"] = dr #1  # Espesor del lecho aguas abajo
segment_data_transient[0]["roughch"] = n_manning #0.1
segment_data_transient[0]["roughbk"] = 0#0.005
segment_data_transient[0]["width1"] = w_bkf#1
segment_data_transient[0]["width2"] = w_bkf#10

#SFR parametrization
sfr = flopy.modflow.ModflowSfr2(
    model,
    nstrm=-reach_data["ireach"].size,  # Número de reachs
    nsfrpar=0,
    nss=segment_data_steady["nseg"].size,  # Número de segmentos
    const=1.0,  # Constante de Manning para flujo en
    dleak=1e-4,  # Tolerancia de fuga
    isfropt=0, # 0 y 1: no flujo vertical NS. P.L. per period. Only when start
    ipakcb=53,
    istcb2=54,
    irtflg =3,
    numtim = 50, # Vital para la estabilidad de la solucion
    weight=.75, # .5 mas dinamico / 1 procesos mas estables
    flwtol=1e-5,
    reach_data=reach_data,
    segment_data={0: segment_data_steady, 1: segment_data_transient},
    transroute=True,
    # nstrail=150,
    # reachinput=True, # Habilitar solamente cuando se define ISFROPT 1
    # itmp  =1,
    tabfiles=True,
    tabfiles_dict=tabfiles_dict,
)
sfr.check()
# sfr.get_outlets()

ibound = np.ones((nlay, nrow, ncol), dtype=int)  # Todas las celdas son activas
# ibound[:, 0, :] = 1        # aguas arriba (i=0)
# ibound[:, -1, :] = 1       # aguas abajo (i=nrow-1)
# ibound[:, :, 0] = 0        # borde izquierdo (j=0)
# ibound[:, :, -1] = 0       # borde derecho (j=ncol-1)
# ibound[-1] = 0  # no-flow floor (celda inactiva)

strt = np.ones((nlay, nrow, ncol), dtype=float)*H_0#(topo - d_bkf + H_0)
ghb_data = []

connection_thickness = D # Distancia del cuerpo de agua a la celda con la condicion impuesta

# for k in range(nlay):           # recorre cada capa
#     for j in range(ncol):       # recorre cada columna (dirección transversal al río)
#         # Borde superior: aguas arriba
#         i_top = 0
#         topo_top = topo[i_top, j]
#         #h_top = topo_top - d_bkf + H_0  + Slope * L
#         h_top = topo_top - d_bkf + H_0
#         cond_top = Ka * w_bkf * delc / connection_thickness
#         ghb_data.append([k, i_top, j, h_top, cond_top, ])

#         # Borde inferior: aguas abajo
#         i_bot = nrow - 1
#         topo_bot = topo[i_bot, j]
#         #h_bot = topo_top - d_bkf + H_0 - Slope * 2 * L
#         h_bot = topo_top - d_bkf + H_0 - Slope * L
#         cond_bot = Ka * w_bkf * delc / connection_thickness
#         ghb_data.append([k, i_bot, j, h_bot, cond_bot])
#         ghb = flopy.modflow.ModflowGhb(
#         model,
#         stress_period_data={0: ghb_data, 1: ghb_data},
#         ipakcb=53)  # Para salida del presupuesto de flujo

for k in range(nlay):
    for j in range(ncol):
        if j != int(ncol/2):  # Excluir columna del río
            # Borde superior
            i_top = 0
            h_top = topo[i_top, j] - d_bkf + H_0
            cond_top = Ka * delr * delc / connection_thickness
            ghb_data.append([k, i_top, j, h_top, cond_top])
            
            # Borde inferior
            i_bot = nrow - 1
            h_bot = topo[i_bot, j] - d_bkf + H_0 - Slope * L
            cond_bot = Ka * delr * delc / connection_thickness
            ghb_data.append([k, i_bot, j, h_bot, cond_bot])

bas = flopy.modflow.ModflowBas(model, ibound=ibound, strt=strt)

#oc - output control, salidas a guardar
stress_period_data = {}
for per in range(len(perlen)):  # n-períodos
    for step in range(nstp[per]):  # pasos en cada período
        stress_period_data[(per, step)] = [
            'save head',
            'save budget'
        ]

oc = flopy.modflow.ModflowOc(
    model,
    stress_period_data=stress_period_data,
    compact=True)

# Escribir y correr el modelo
model.write_input()
success, buff = model.run_model(silent=False, report=True)`

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions