SHELL := /bin/bash
CUDA_PATH ?= /usr/local/cuda
GPU_CAP_RAW := $(shell bash -c 'if command -v nvidia-smi >/dev/null 2>&1; then nvidia-smi --query-gpu=compute_cap --format=csv,noheader | head -n 1; fi')
GPU_CAP := $(strip $(GPU_CAP_RAW))
GPU_ARCH := $(if $(GPU_CAP),sm_$(subst .,,$(GPU_CAP)))
ARCH ?= $(if $(GPU_ARCH),$(GPU_ARCH),sm_86)
JOBS ?= $(shell nproc 2>/dev/null || sysctl -n hw.ncpu 2>/dev/null || echo 4)
KERNEL_DIR := main/langevin-gpu
KERNEL_ARTIFACTS := $(KERNEL_DIR)/lib/kernel.cubin $(KERNEL_DIR)/lib/kernel.ptx
MICROBLOOM_GRAPH := external/microBlooM/output/vascular_hex.pkl
VASCULAR_GRAPH := experiments/vascular_hex/vascular_hex_graph.npz
MICROBLOOM_REAL_GRAPH := experiments/vascular_hex/microbloom_real_graph.npz
MICROBLOOM_REAL_FLOW_GRAPH := experiments/vascular_hex/microbloom_real_flow_graph.npz

DUNE_ROOT := external
DUNE_CORE_MODULES := common geometry grid localfunctions istl
DUNE_EXT_MODULES := foamgrid
DUNE_DEPS_STAMP := $(DUNE_ROOT)/.dune-deps
DUMUX_DIR := $(DUNE_ROOT)/dumux
DUMUX_BUILD := $(DUMUX_DIR)/build-cmake
DUMUX_EXAMPLE := $(abspath $(DUMUX_BUILD)/examples/network_tracer_1d/example_network_tracer_1d)
DUMUX_EXAMPLE_DIR := $(dir $(DUMUX_EXAMPLE))
DUMUX_PARAMS := $(abspath $(DUMUX_BUILD)/examples/network_tracer_1d/params.input)
DUMUX_DGF_SRC := external/dumux/examples/network_tracer_1d/network_filtered.dgf
DUMUX_DGF_BUILD := $(abspath $(DUMUX_BUILD)/examples/network_tracer_1d/network_filtered.dgf)
DUMUX_VTK_DIR ?= $(DUMUX_BUILD)/examples/network_tracer_1d
DUMUX_METRIC_LARGE_OUTPUT ?= data/dumux_metric_graph_sim_large.npz
DUMUX_INPUT_NPZ ?= data/dumux_network_input.npz
DUMUX_CACHE := $(DUMUX_BUILD)/CMakeCache.txt
DUMUX_MIN_CELLS_PER_MIN_EDGE ?= 10

# Python config modules (diffusion by default; override for advection)
DUMUX_SETTINGS_MODULE ?= experiments.dumux_tracer.experiment_settings
DUMUX_CONFIG_MODULE ?= experiments.dumux_tracer.metric_graph_config
DUMUX_COMPARE_MODULE ?= experiments.dumux_tracer.compare_config
DUMUX_ENV := DUMUX_SETTINGS_MODULE=$(DUMUX_SETTINGS_MODULE) DUMUX_CONFIG_MODULE=$(DUMUX_CONFIG_MODULE) DUMUX_COMPARE_MODULE=$(DUMUX_COMPARE_MODULE)

.PHONY: kernel run grid plot vascular-convert vascular-run vascular-real vascular-real-flow clean print-arch \
	dumux-deps dumux-build dumux-preprocess dumux-run dumux-fvm dumux-extract dumux-extract-small dumux-metric dumux-metric-large \
	dumux-plots dumux-plots-large dumux-density-gif dumux-density-gif-large dumux-compare dumux-edge-profiles \
	dumux-advection-preprocess dumux-advection-run dumux-advection-fvm dumux-advection-metric dumux-advection-compare dumux-advection-plots \
	dumux-diffusion-pipeline dumux-advection-pipeline dumux-all

kernel: $(KERNEL_ARTIFACTS)

$(KERNEL_ARTIFACTS):
	@echo "Building CUDA kernels for $(ARCH)"
	@$(MAKE) -C $(KERNEL_DIR) ARCH=$(ARCH) CUDA_PATH=$(CUDA_PATH)

run: $(KERNEL_ARTIFACTS)
	cd main && sorcerun run main.py config.py

grid: $(KERNEL_ARTIFACTS)
	cd main && sorcerun grid-run main.py grid_config.py

plot:
	python plot.py

$(VASCULAR_GRAPH): $(MICROBLOOM_GRAPH)
	python -m experiments.vascular_hex.convert_microbloom_network --input $(MICROBLOOM_GRAPH) --output $(VASCULAR_GRAPH)

vascular-convert: $(VASCULAR_GRAPH)

vascular-run: kernel $(VASCULAR_GRAPH)
	python -m experiments.vascular_hex.run_vascular_hex

$(MICROBLOOM_REAL_GRAPH): experiments/vascular_hex/microbloom_real.pkl
	python -m experiments.vascular_hex.convert_microbloom_network --input experiments/vascular_hex/microbloom_real.pkl --output $(MICROBLOOM_REAL_GRAPH)

vascular-real: kernel $(MICROBLOOM_REAL_GRAPH)
	python -m experiments.vascular_hex.run_vascular_real

$(MICROBLOOM_REAL_FLOW_GRAPH): experiments/vascular_hex/microbloom_real.pkl
	python -m experiments.vascular_hex.convert_microbloom_network --use_flow_as_drift --input experiments/vascular_hex/microbloom_real.pkl --output $(MICROBLOOM_REAL_FLOW_GRAPH)

vascular-real-flow: kernel $(MICROBLOOM_REAL_FLOW_GRAPH)
	python -m experiments.vascular_hex.run_vascular_real_flow

clean:
	@$(MAKE) -C $(KERNEL_DIR) clean
	rm -f $(KERNEL_ARTIFACTS)
	rm -f $(DUMUX_METRIC_LARGE_OUTPUT) $(DUMUX_INPUT_NPZ) $(DUMUX_DGF_SRC)
	rm -f figs/dumux_*.png figs/dumux_*.gif figs/metric_*.gif figs/*dumux*.gif figs/*metric*.gif
	rm -rf figs/diffusion figs/advection figs/summary
	rm -f data/dumux_*metric_graph_sim*.npz data/dumux_*tracer_1d*.npz data/dumux_*network_input*.npz data/*runtime*.txt
	rm -f data/p_*.npy data/hists_*.npy
	rm -rf external/dumux/build-cmake/examples/network_tracer_1d/network_tracer_1d-*.vt? external/dumux/build-cmake/examples/network_tracer_1d/*tracer_amounts.dat external/dumux/build-cmake/examples/network_tracer_1d/network_tracer_1d.pvd

print-arch:
	@echo $(ARCH)

dumux-deps: $(DUNE_DEPS_STAMP)
	@echo "DUNE/DuMuX sources present under $(DUNE_ROOT)"

$(DUNE_DEPS_STAMP):
	@mkdir -p "$(DUNE_ROOT)"
	@for module in $(DUNE_CORE_MODULES); do \
		if [ ! -d "$(DUNE_ROOT)/dune-$$module" ]; then \
			git clone --branch releases/2.10 --depth 1 https://gitlab.dune-project.org/core/dune-$$module.git "$(DUNE_ROOT)/dune-$$module"; \
		else \
			echo "dune-$$module already present at $(DUNE_ROOT)/dune-$$module"; \
		fi; \
	done
	@for module in $(DUNE_EXT_MODULES); do \
		if [ ! -d "$(DUNE_ROOT)/dune-$$module" ]; then \
			git clone --branch releases/2.10 --depth 1 https://gitlab.dune-project.org/extensions/dune-$$module.git "$(DUNE_ROOT)/dune-$$module"; \
		else \
			echo "dune-$$module already present at $(DUNE_ROOT)/dune-$$module"; \
		fi; \
	done
	@if [ ! -d "$(DUMUX_DIR)" ]; then \
		git clone https://git.iws.uni-stuttgart.de/dumux-repositories/dumux.git "$(DUMUX_DIR)"; \
	else \
		echo "DuMuX already present at $(DUMUX_DIR)"; \
	fi
	@touch "$(DUNE_DEPS_STAMP)"

$(DUMUX_CACHE): | $(DUNE_DEPS_STAMP)
	@if [ ! -f "$(DUMUX_CACHE)" ]; then \
		cd "$(DUNE_ROOT)" && ./dune-common/bin/dunecontrol --opts=dumux/cmake.opts all; \
	fi

dumux-preprocess: dumux-deps
	$(DUMUX_ENV) python -m experiments.dumux_tracer.preprocess_network \
		--min-cells-per-min-edge $(DUMUX_MIN_CELLS_PER_MIN_EDGE) \
		$(if $(DUMUX_TARGET_DX),--target-dx $(DUMUX_TARGET_DX),) \
		$(if $(DUMUX_FLUX_SCALE),--flux-scale $(DUMUX_FLUX_SCALE),) \
		$(if $(DUMUX_SYNTHETIC_VELOCITY),--synthetic-velocity $(DUMUX_SYNTHETIC_VELOCITY),)
	@mkdir -p "$(dir $(DUMUX_DGF_BUILD))"
	cp "$(DUMUX_DGF_SRC)" "$(DUMUX_DGF_BUILD)"
	@echo "Copied filtered grid to $(DUMUX_DGF_BUILD)"

dumux-configure: $(DUMUX_CACHE)
	@echo "DuMuX configured at $(DUMUX_BUILD)"

dumux-build: $(DUMUX_CACHE)
	cmake --build "$(DUMUX_BUILD)" --target example_network_tracer_1d -j $(JOBS)

dumux-clean-output:
	find "$(DUMUX_EXAMPLE_DIR)" -maxdepth 1 -name 'network_tracer_1d-*.vt*' -delete
	find "$(DUMUX_EXAMPLE_DIR)" -maxdepth 1 -name '*tracer_amounts.dat' -delete
	rm -f "$(DUMUX_EXAMPLE_DIR)/network_tracer_1d.pvd"

dumux-run: dumux-preprocess dumux-build
	$(MAKE) dumux-clean-output
	$(DUMUX_ENV) \
	DUMUX_EXAMPLE_DIR="$(DUMUX_EXAMPLE_DIR)" \
	DUMUX_EXE="$(DUMUX_EXAMPLE)" \
	DUMUX_PARAMS_PATH="$(DUMUX_PARAMS)" \
	python -m experiments.dumux_tracer.run_dumux_example

dumux-fvm: dumux-run
	$(DUMUX_ENV) python -m experiments.dumux_tracer.extract_dumux_tracer

dumux-extract: dumux-fvm

dumux-extract-small: dumux-run
	$(DUMUX_ENV) DUMUX_TRACER_MAX_STEPS=20 python -m experiments.dumux_tracer.extract_dumux_tracer

dumux-metric: $(KERNEL_ARTIFACTS)
	$(DUMUX_ENV) python -m experiments.dumux_tracer.run_metric_graph

# Advection (nonzero drift) targets
ADVECTION_SETTINGS_MODULE := experiments.dumux_tracer.advection_settings
ADVECTION_CONFIG_MODULE := experiments.dumux_tracer.advection_metric_graph_config
ADVECTION_COMPARE_MODULE := experiments.dumux_tracer.compare_config
ADVECTION_ENV := DUMUX_SETTINGS_MODULE=$(ADVECTION_SETTINGS_MODULE) DUMUX_CONFIG_MODULE=$(ADVECTION_CONFIG_MODULE) DUMUX_COMPARE_MODULE=$(ADVECTION_COMPARE_MODULE)

dumux-advection-preprocess: dumux-deps
	$(ADVECTION_ENV) python -m experiments.dumux_tracer.preprocess_network \
		--min-cells-per-min-edge $(DUMUX_MIN_CELLS_PER_MIN_EDGE) \
		$(if $(DUMUX_TARGET_DX),--target-dx $(DUMUX_TARGET_DX),) \
		$(if $(DUMUX_FLUX_SCALE),--flux-scale $(DUMUX_FLUX_SCALE),) \
		$(if $(DUMUX_SYNTHETIC_VELOCITY),--synthetic-velocity $(DUMUX_SYNTHETIC_VELOCITY),)
	@mkdir -p "$(dir $(DUMUX_DGF_BUILD))"
	cp "$(DUMUX_DGF_SRC)" "$(DUMUX_DGF_BUILD)"
	@echo "Copied filtered grid to $(DUMUX_DGF_BUILD)"

dumux-advection-run: dumux-advection-preprocess dumux-build
	$(MAKE) dumux-clean-output
	$(ADVECTION_ENV) \
	DUMUX_EXAMPLE_DIR="$(DUMUX_EXAMPLE_DIR)" \
	DUMUX_EXE="$(DUMUX_EXAMPLE)" \
	DUMUX_PARAMS_PATH="$(DUMUX_PARAMS)" \
	python -m experiments.dumux_tracer.run_dumux_example

dumux-advection-fvm: dumux-advection-run
	$(ADVECTION_ENV) python -m experiments.dumux_tracer.extract_dumux_tracer

dumux-advection-metric: $(KERNEL_ARTIFACTS)
	$(ADVECTION_ENV) python -m experiments.dumux_tracer.run_metric_graph

dumux-advection-metric-gifs:
	$(ADVECTION_ENV) python -m experiments.dumux_tracer.plot_metric_only

dumux-advection-compare:
	$(ADVECTION_ENV) python -m experiments.dumux_tracer.compare_metric_to_dumux

dumux-advection-plots:
	$(ADVECTION_ENV) python -m experiments.dumux_tracer.plot_diffusion_compare
	$(ADVECTION_ENV) python -m experiments.dumux_tracer.plot_runtime
	$(ADVECTION_ENV) python -m experiments.dumux_tracer.plot_velocity_mass

dumux-metric-large: $(KERNEL_ARTIFACTS)
	DUMUX_MG_OUTPUT="$(DUMUX_METRIC_LARGE_OUTPUT)" \
	DUMUX_MG_PARTICLES=4000000 \
	DUMUX_MG_NUM_BINS=160 \
	DUMUX_MG_DT=5e-4 \
	DUMUX_MG_STEPS=600 \
	DUMUX_MG_RECORD_INTERVAL=10 \
	python -m experiments.dumux_tracer.run_metric_graph

dumux-compare:
	$(DUMUX_ENV) python -m experiments.dumux_tracer.compare_metric_to_dumux

dumux-plots:
	$(DUMUX_ENV) python -m experiments.dumux_tracer.plot_diffusion_compare
	$(DUMUX_ENV) python -m experiments.dumux_tracer.plot_runtime

dumux-diffusion-mg-gif:
	$(DUMUX_ENV) python -m experiments.dumux_tracer.plot_diffusion_mg_only

dumux-plots-large:
	DUMUX_MG_OUTPUT="$(DUMUX_METRIC_LARGE_OUTPUT)" python -m experiments.dumux_tracer.plot_diffusion_compare

dumux-runtime-plot:
	$(DUMUX_ENV) python -m experiments.dumux_tracer.plot_runtime

dumux-summary-plots:
	python -m experiments.dumux_tracer.plot_summary

# Revision targets
.PHONY: revision-diffusion-data revision-diffusion-plots revision-drift-data revision-drift-plots revision-all

revision-diffusion-data:
	python -m experiments.dumux_tracer.revision_diffusion

revision-diffusion-plots:
	@if [ -z "$(MANIFEST)" ]; then \
		latest=$$(ls -1dt data/revision/diffusion/*/manifest.json 2>/dev/null | head -n1); \
		[ -n "$$latest" ] || { echo "No diffusion manifest found. Run revision-diffusion-data first."; exit 1; }; \
		echo "python -m experiments.dumux_tracer.revision_diffusion_plots --manifest $$latest"; \
		python -m experiments.dumux_tracer.revision_diffusion_plots --manifest $$latest; \
	else \
		echo "python -m experiments.dumux_tracer.revision_diffusion_plots --manifest $(MANIFEST)"; \
		python -m experiments.dumux_tracer.revision_diffusion_plots --manifest $(MANIFEST); \
	fi

# Generate GIFs for all runs in a diffusion manifest (FVM + MG)
.PHONY: revision-diffusion-gifs
revision-diffusion-gifs:
	@if [ -z "$(MANIFEST)" ]; then \
		latest=$$(ls -1dt data/revision/diffusion/*/manifest.json 2>/dev/null | head -n1); \
		[ -n "$$latest" ] || { echo "No diffusion manifest found. Run revision-diffusion-data first."; exit 1; }; \
		python -m experiments.dumux_tracer.revision_diffusion_gifs --manifest $$latest; \
	else \
		python -m experiments.dumux_tracer.revision_diffusion_gifs --manifest $(MANIFEST); \
	fi

revision-drift-data:
	python -m experiments.dumux_tracer.revision_drift

revision-drift-plots:
	@if [ -z "$(MANIFEST)" ]; then \
		latest=$$(ls -1dt data/revision/drift/*/manifest.json 2>/dev/null | head -n1); \
		[ -n "$$latest" ] || { echo "No drift manifest found. Run revision-drift-data first."; exit 1; }; \
		python -m experiments.dumux_tracer.revision_drift_plots --manifest $$latest; \
	else \
		python -m experiments.dumux_tracer.revision_drift_plots --manifest $(MANIFEST); \
	fi

.PHONY: revision-drift-mg-density
revision-drift-mg-density:
	@if [ -z "$(MANIFEST)" ]; then \
		latest=$$(ls -1dt data/revision/drift/*/manifest.json 2>/dev/null | head -n1); \
		[ -n "$$latest" ] || { echo "No drift manifest found. Run revision-drift-data first."; exit 1; }; \
		python -m experiments.dumux_tracer.revision_drift_mg_density --manifest $$latest; \
	else \
		python -m experiments.dumux_tracer.revision_drift_mg_density --manifest $(MANIFEST); \
	fi

.PHONY: revision-drift-fvm-density
revision-drift-fvm-density:
	@if [ -z "$(MANIFEST)" ]; then \
		latest=$$(ls -1dt data/revision/drift/*/manifest.json 2>/dev/null | head -n1); \
		[ -n "$$latest" ] || { echo "No drift manifest found. Run revision-drift-data first."; exit 1; }; \
		python -m experiments.dumux_tracer.revision_drift_fvm_density --manifest $$latest; \
	else \
		python -m experiments.dumux_tracer.revision_drift_fvm_density --manifest $(MANIFEST); \
	fi

.PHONY: revision-drift-gifs
revision-drift-gifs:
	@if [ -z "$(MANIFEST)" ]; then \
		latest=$$(ls -1dt data/revision/drift/*/manifest.json 2>/dev/null | head -n1); \
		[ -n "$$latest" ] || { echo "No drift manifest found. Run revision-drift-data first."; exit 1; }; \
		python -m experiments.dumux_tracer.revision_drift_gifs --manifest $$latest; \
	else \
		python -m experiments.dumux_tracer.revision_drift_gifs --manifest $(MANIFEST); \
	fi

revision-all: revision-diffusion-data revision-diffusion-plots revision-drift-data revision-drift-plots

# Convenience pipelines
dumux-diffusion-pipeline: dumux-fvm dumux-metric dumux-compare dumux-plots

dumux-advection-pipeline: dumux-advection-fvm dumux-advection-metric dumux-advection-compare dumux-advection-plots

dumux-all: dumux-diffusion-pipeline dumux-advection-pipeline dumux-summary-plots

dumux-density-gif:
	python -m experiments.dumux_tracer.plot_density_evolution

dumux-density-gif-large:
	DUMUX_MG_OUTPUT="$(DUMUX_METRIC_LARGE_OUTPUT)" python -m experiments.dumux_tracer.plot_density_evolution

dumux-edge-profiles:
	python -m experiments.dumux_tracer.plot_edge_profiles

dumux-fvm-gifs:
	python -m experiments.dumux_tracer.plot_dumux_baseline

dumux-network-stats:
	python -m experiments.dumux_tracer.network_stats

dumux-profile-gif:
	python -m experiments.dumux_tracer.plot_edge_profile_gif

dumux-profile-gif-dumux:
	python -m experiments.dumux_tracer.plot_edge_profile_gif dumux

dumux-metric-sorcerun: $(KERNEL_ARTIFACTS)
	sorcerun run experiments/dumux_tracer/sorcerun_main.py experiments/dumux_tracer/sorcerun_config.py
