Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .streamlit/config.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[server]
headless = true
address = "0.0.0.0"
port = 8501
port = 5000
enableCORS = true
enableXsrfProtection = true

Expand Down
Binary file added logo_SISSA-1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added logo_inest_spoke9.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
268 changes: 217 additions & 51 deletions rom.py
Original file line number Diff line number Diff line change
@@ -1,77 +1,243 @@
import sys
import numpy as np
import streamlit as st
import cloudpickle
import matplotlib.pyplot as plt
from matplotlib.colors import LogNorm
from scipy.ndimage import gaussian_filter
from mpl_toolkits.axes_grid1 import make_axes_locatable
from pathlib import Path
from datetime import date
import datetime

today = date.today()
lat = np.load('required_data/latitude.npy')
long = np.load('required_data/longitude.npy')

# ---------------- CONFIG ----------------
st.set_page_config(page_title="AdriaSeaWatch", page_icon="ogs_logo.png", layout="wide")

BASE_DIR = Path(__file__).resolve().parent
DAILY_DIR = BASE_DIR / "daily_data"
MODELS_DIR = BASE_DIR / "models"

st.set_page_config(
page_title="AdriaSeaWatch",
page_icon="ogs_logo.png"
)
Po = np.linspace(200, 4000, 10000)
Isonzo = np.linspace(0, 500, 10000)
Timavo = np.linspace(8, 80, 10000)

variables_list = ["N1p","N3n","S","T","Chla"]
variables_name = {
"N1p":r"Phospates [$$\text{mg}/\text{m}^3$$]",
"N3n":r"Nitrates [$$\text{mg}/\text{m}^3$$]",
"S":r"Salinity",
"T":r"Temperature [$$^\circ C$$]",
"Chla":r"Chlorophyll [$$\text{mg}/\text{m}^3$$]"
}
depth_list = [0, 10] # metri
days_list = [0,1,2,3,4,5]

Po=np.linspace(200,4000,10000)
Isonzo=np.linspace(0,500,10000)
Timavo=np.linspace(8,80,10000)
variables=["N1p","N3n","S","T","Chla"]
depth={"O m":0, "10 m":1, "30 m":2, "60 m":3, "100 m":4}
depth_dict = {0:0, 10:1}

vmins={"N1p":0,"N3n": 0, "Chla":0, "S":30, "T":13}
vmaxs={"N1p":0.2,"N3n": 10, "Chla":4, "S":39.5, "T":30}
vmins = {"N1p":0,"N3n":0,"Chla":0,"S":30,"T":13}
vmaxs = {"N1p":0.2,"N3n":10,"Chla":4,"S":39.5,"T":30}

# --- CONFIGURE YOUR CHOICES ---
depth_list = [0,10]
variables_list=["N1p","N3n","S","T","Chla"]
days_list=[0,1,2,3,4,5]
# ---------------- STATE INIT ----------------
def init_state():
if "day_idx" not in st.session_state:
st.session_state.day_idx = 0
if "var_idx" not in st.session_state:
st.session_state.var_idx = 0
if "depth_idx" not in st.session_state:
st.session_state.depth_idx = 0
if "po" not in st.session_state:
st.session_state.po = float(Po[-1]/2)
if "isonzo" not in st.session_state:
st.session_state.isonzo = float(Isonzo[-1]/2)
if "timavo" not in st.session_state:
st.session_state.timavo = float(Timavo[-1]/2)

depth_dict={
0:0,
10:1,
# 30:2,
# 60:3,
# 100:4
init_state()

}
# ---------------- ACTIONS ----------------
def prev_day():
st.session_state.day_idx = max(0, st.session_state.day_idx - 1)

# --- WIDGETS ---
st.sidebar.title("Controls")
days_ctx = st.sidebar.selectbox("Days", days_list, index=0)
variables_ctx = st.sidebar.selectbox("Variable", variables_list, index=0)
depth_ctx = st.sidebar.selectbox("Depth", depth_list, index=0)
def next_day():
st.session_state.day_idx = min(len(days_list)-1, st.session_state.day_idx + 1)

po= st.sidebar.slider("Po", min_value=Po[0], max_value=Po[-1], value=Po[-1]/2)
isonzo = st.sidebar.slider("Isonzo", min_value=Isonzo[0], max_value=Isonzo[-1], value=Isonzo[-1]/2)
timavo = st.sidebar.slider("Timavo", min_value=Timavo[0], max_value=Timavo[-1], value=Timavo[-1]/2)
def set_var(i):
st.session_state.var_idx = i

# --- MODEL PREDICTION (placeholder) ---
def set_depth(i):
st.session_state.depth_idx = i

# ---------------- MODEL PREDICT ----------------
def model_predict(days, variable, depth, po, isonzo, timavo):
tmp=np.load(DAILY_DIR / "{}_{}.npy".format(variable,depth))
model_filename = "model_{}_{}.pkl".format(variable, depth_dict[depth])
model_path = MODELS_DIR / "model_{}".format(variable) / model_filename
tmp = np.load(DAILY_DIR / f"{variable}_{depth}.npy")
model_filename = f"model_{variable}_{depth_dict[depth]}.pkl"
model_path = MODELS_DIR / f"model_{variable}" / model_filename

with open(model_path, 'rb') as f:
with open(model_path, "rb") as f:
model = cloudpickle.load(f)
output=model(tmp, isonzo, po, timavo, days)
output = model(tmp, isonzo, po, timavo, days)
print(f"Predicted {variable} at {depth}m for day {days} with Po={po}, Isonzo={isonzo}, Timavo={timavo}")
print(output.mean())
return output

# Compute
Z = model_predict(days_ctx, variables_ctx, depth_ctx, po, isonzo, timavo)
# ---------------- CURRENT CONTEXT ----------------
days_ctx = days_list[st.session_state.day_idx]
variables_ctx = variables_list[st.session_state.var_idx]
depth_ctx = depth_list[st.session_state.depth_idx]

po = st.session_state.po
isonzo = st.session_state.isonzo
timavo = st.session_state.timavo

# ---------------- LAYOUT ----------------
# 3 colonne: bottoni variabile | plot | bottoni profondità

main = st.container(horizontal=True)
left, mid, right = main.columns([1.5, 6, 1.2], gap="large", )

with left:
fdate = today.strftime('%d/%m/%Y')
st.markdown("### AdriaSeaWatch ROM Explorer")
st.markdown(f"Analysis performed on: {fdate}")
st.markdown("##### Model Description")
st.html("""
<span style='font-size: 0.9rem; text-align: justify;'>
The model is a data-driven reduced-order forecasting system composed of three autoregressive models, each operating on a different sub-region of the domain. Temporal evolution is learned using Gaussian Process–based autoregression. Unlike traditional forecasting models, it does not rely on physical equations or numerical solvers, but learns the system dynamics directly from historical data.
</br>


The model does not require explicit boundary conditions. The initial conditions are given by the spatial field at the bulletin day and by the river discharge values selected by the user, which drive the subsequent temporal evolution.
</span>
"""
)
logo1, logo2, logo3 = st.columns(3, vertical_alignment="center")
logo1.image("ogs_logo.png", width=60)
logo2.image("logo_SISSA-1.png", width=100)
logo3.image("logo_inest_spoke9.png", width=100)


with right:
st.markdown("##### Field of Interest")
for i, v in enumerate(variables_list):
st.button(
variables_name[v],
use_container_width=True,
type="primary" if i == st.session_state.var_idx else "secondary",
on_click=set_var,
args=(i,),
key=f"var_{v}"
)
right.markdown(r"##### Depth [$$\text{m}$$]")
for i, d in enumerate(depth_list):
st.button(
f"{d} m",
use_container_width=True,
type="primary" if i == st.session_state.depth_idx else "secondary",
on_click=set_depth,
args=(i,),
key=f"depth_{d}"
)

st.markdown(r"##### River Discharge [$${\text{m}^3}/\text{s}$$]")
st.session_state.depth_idx = depth_list.index(depth_ctx)
# slider sotto i bottoni temporali
# st.session_state.po = st.slider("Po", float(Po[0]), float(Po[-1]), float(st.session_state.po))
# st.session_state.isonzo = st.slider("Isonzo", float(Isonzo[0]), float(Isonzo[-1]), float(st.session_state.isonzo))
# st.session_state.timavo = st.slider("Timavo", float(Timavo[0]), float(Timavo[-1]), float(st.session_state.timavo))

disable_sliders = (st.session_state.day_idx == 0)

po = st.slider(
"Po",
float(Po[0]), float(Po[-1]),
value=float(st.session_state.po),
key="po",
disabled=disable_sliders
)

isonzo = st.slider(
"Isonzo",
float(Isonzo[0]), float(Isonzo[-1]),
value=float(st.session_state.isonzo),
key="isonzo",
disabled=disable_sliders
)

timavo = st.slider(
"Timavo",
float(Timavo[0]), float(Timavo[-1]),
value=float(st.session_state.timavo),
key="timavo",
disabled=disable_sliders
)

po = st.session_state.po
isonzo = st.session_state.isonzo
timavo = st.session_state.timavo

with mid:
# calcolo e plot in mezzo
Z = model_predict(days_ctx, variables_ctx, depth_ctx, po, isonzo, timavo)
Zma = np.ma.asarray(Z) # lo tratto come masked array comunque
mask = np.ma.getmaskarray(Zma) # True dove c'è "--"
Zplot = Zma.astype(object) # fondamentale: altrimenti None non entra
Zplot[mask] = None # masked -> None


import plotly.graph_objects as go

fig = go.Figure(
data=go.Heatmap(
z=Zplot[::-1], # Inverti l'asse y per avere l'origine in basso
x=long,
y=lat,
colorscale="Jet",
zmin=vmins[variables_ctx],
zmax=vmaxs[variables_ctx],
# colorbar=dict(title=variables_name[variables_ctx]),
hovertemplate='Lat: %{y}<br>Lon: %{x}<br>Value: %{z}<extra></extra>'
)
)

fig.update_layout(
height=600,
yaxis=dict(autorange="reversed"), # ORIGIN LOWER
xaxis=dict(constrain="domain"),
margin=dict(l=20, r=20, t=20, b=20)
)

fig.update_yaxes(
scaleanchor="x",
scaleratio=1
)
st.plotly_chart(fig,
config={
"responsive": True
}
)
# fig, ax = plt.subplots(figsize=(12, 6), dpi=150)
# divider = make_axes_locatable(ax)
# cax = divider.append_axes('right', size='3%', pad=0.05)

# im = ax.imshow(
# Z, origin="lower", aspect="auto", cmap="jet",
# vmin=vmins[variables_ctx], vmax=vmaxs[variables_ctx]
# )
# fig.colorbar(im, cax=cax, orientation="vertical")
# ax.set_title(f"Giorno {days_ctx} | {variables_ctx} | {depth_ctx} m")
# fig.tight_layout()

# st.pyplot(fig, use_container_width=True)

# --- PLOT ---
fig, ax = plt.subplots()
divider = make_axes_locatable(ax)
cax = divider.append_axes('right', size='5%', pad=0.05)
# im=ax.imshow(Z, origin='lower', aspect='auto', cmap="Pastel1")
im=ax.imshow(Z, origin='lower', aspect='auto', cmap="jet",vmin=vmins[variables_ctx],vmax=vmaxs[variables_ctx])
#ax.set_title(f"{days_ctx} | ctx1={variables_ctx}, ctx2={depth_ctx} | a={po}, b={isonzo}, c={timavo}")
fig.colorbar(im, cax=cax, orientation='vertical')
# CONTROLLI IN BASSO (sotto al plot): tempo + slider
delta = datetime.timedelta(days=days_ctx)
fdate = (today + delta).strftime('%d/%m/%Y')
b1, b2, b3 = st.columns([1,2,1])

# --- DISPLAY ---
st.pyplot(fig)
with b1:
st.button("◀ Prev", use_container_width=True, on_click=prev_day, disabled=(st.session_state.day_idx == 0))
with b2:
st.markdown(f"<div style='text-align:center; font-size:18px;'><b>Day: {fdate}</b></div>", unsafe_allow_html=True)
with b3:
st.button("Next ▶", use_container_width=True, on_click=next_day, disabled=(st.session_state.day_idx == len(days_list)-1))