Julia Growth-Model Toolbox — Setup & Reference Notes
=======================================================


PART 1: ONE-TIME SETUP
=======================================================

1. Open terminal, check Julia is installed:

    julia --version

   If this fails, install Julia from https://julialang.org/downloads/
   (check "Add Julia to PATH" during setup), then reopen your terminal
   and try again.


2. Download/clone the toolbox from GitHub, then navigate to that folder.
   Replace the path below with wherever YOU saved it:

    cd "C:\path\to\your\JuliaGrowth"
    dir

   Confirm you see: GrowthModels.jl, FitGrowthModel.jl,
   BootstrapUncertainty.jl, PlotGrowthFit.jl, Identifiability.jl,
   run_example.jl, edit_plot.jl, jalisco.txt


3. Open Julia:

    julia


4. Create and activate a LOCAL environment for this project, then
   install all required packages into it. This keeps this toolbox's
   package versions separate from anything else you use Julia for
   (other classes, other research projects, etc.) — IMPORTANT: skipping
   this step and installing packages globally is the most common cause
   of confusing version-conflict errors later, especially if you also
   use Julia for other coursework/research.

   Do this once per folder (will take a while the first time —
   several minutes, be patient):

    cd(raw"C:\path\to\your\JuliaGrowth")
    using Pkg
    Pkg.activate(".")
    Pkg.add(["DifferentialEquations", "Optimization", "OptimizationNLopt",
             "SpecialFunctions", "Distributions", "Plots", "CSV",
             "DataFrames", "JLD2", "ADTypes", "ForwardDiff"])

   This creates a Project.toml and Manifest.toml file INSIDE this
   folder — these files record exactly which package versions this
   project uses. Don't delete them; they belong to this project.

   (No need to install SciMLBase separately — not required, and
   installing it can cause version conflicts with other packages
   like ModelingToolkit.)

If you have already completed first 4 steps then you only need the following 
steps for new data model fit after new session

5. Restart Julia after installing (close and reopen). EVERY TIME you
   come back to work on this toolbox (not just the first time), run
   these two lines first to make sure you're using the right
   environment:

    cd(raw"C:\path\to\your\JuliaGrowth")
    using Pkg
    Pkg.activate(".")

   Forgetting Pkg.activate(".") will silently fall back to your
   global Julia environment, which can cause the exact version
   conflicts this setup is designed to avoid.


6. Run the script:

    include("run_example.jl")


PART 2: EDITING run_example.jl — WHAT YOU CAN CHANGE
=======================================================

Near the top of the "Load data" and "Point-estimate fit" sections, these
are the variables you'll typically want to edit for a new run:

    dataset_name        -- short label used in every output filename
    datafile             -- path to your input .txt file (2 columns:
                             time, incidence; tab-separated, no header)
    calibration_period   -- number of time points used to FIT the model
    forecast_horizon     -- number of weeks ahead to forecast (0 = fit
                             only, no forecast). Needs ydata_all to have
                             at least calibration_period + forecast_horizon
                             rows if you also want forecast_performance
                             computed against real held-out data.
    flag                 -- which growth model to fit (see table below)
    dist                 -- which error structure to assume (see table
                             below)
    M                    -- number of bootstrap replicates (default 300;
                             more = smoother CIs but slower)


-------------------------------------------------------
GROWTH MODEL SYMBOLS (the `flag` variable)
-------------------------------------------------------

  flag     Name    ODE                                  Free parameters
  ------   ----    ----------------------------------    ---------------
  :exp     EXP     dx = r*x                              r
  :ggm     GGM     dx = r*x^p                             r, p
  :glm     GLM     dx = r*x^p*(1 - x/K)                   r, p, K
  :grm     GRM     dx = r*x^p*(1 - (x/K)^a)                r, p, a, K
  :lm      LM      dx = r*x*(1 - x/K)                     r, K
  :rich    RICH    dx = r*x*(1 - (x/K)^a)                  r, a, K
  :gom     GOM     dx = r*x*exp(-a*t)   (no K term)        r, a

  Example:  flag = :grm     (Generalized Richards Model)


-------------------------------------------------------
ERROR STRUCTURE SYMBOLS (the `dist` variable)
-------------------------------------------------------

  dist       Meaning                          Variance assumption
  --------   -----------------------------    -----------------------
  :normal    Normal / least-squares            constant variance
  :poisson   Poisson                           Var = mean
  :nb1       Negative binomial (NB1)            Var = mean + alpha*mean
             (overdispersed count data —
              usually the best default for
              real epidemic case counts)

  Example:  dist = :nb1

  Note: GRM (and, to a lesser extent, RICH) can be structurally
  collinear under NB1 on some datasets — if a fit looks unstable
  (huge K, wide/multimodal parameter histograms), try GLM, LM, or RICH
  first to sanity-check, or increase n_restarts in the call to
  fit_growth_model(...).


-------------------------------------------------------
OUTPUT FILES
-------------------------------------------------------

Everything is saved to the "output" subfolder, named as:
<Dataset>_<Model>_<ErrorStructure>_...

  e.g. for dataset_name="JALISCO", flag=:grm, dist=:nb1 :

    JALISCO_GRM_nb1_fit_forecast.png              -- plot: histograms +
                                                      fit/forecast band
    JALISCO_GRM_nb1_parameters.csv                -- r, p, a, K medians
                                                      + 95% CI
    JALISCO_GRM_nb1_AICc.csv                       -- AICc, log-likelihood,
                                                      parameter count
    JALISCO_GRM_nb1_calibration_performance.csv    -- MAE, MSE, 95% PI
                                                      coverage, WIS on the
                                                      calibration window
    JALISCO_GRM_nb1_forecast_performance.csv       -- same metrics, one
                                                      row per forecast
                                                      step, vs. held-out
                                                      data (only produced
                                                      if forecast_horizon>0
                                                      and enough data
                                                      exists)
    JALISCO_GRM_nb1_fit_object.jld2                -- full saved object
                                                      (point_fit, boot,
                                                      data) — reload with
                                                      @load for re-plotting
                                                      or further analysis
                                                      without re-fitting


-------------------------------------------------------
TROUBLESHOOTING
-------------------------------------------------------

If you see: "UndefVarError ... two or more modules export different
bindings with this name, resulting in ambiguity" — this means two
modules define the same function/variable name. Fix by qualifying the
call with its defining module, e.g.:

    FitGrowthModel.fit_growth_model(...)   instead of   fit_growth_model(...)
    BootstrapUncertainty.run_bootstrap(...) instead of  run_bootstrap(...)
    GrowthModels.MODEL_NAMES[flag]          instead of  MODEL_NAMES[flag]

If a fit fails to converge, the script will now print a full Julia error
message (not just "failed to converge") — read the first few lines of the
stack trace for the actual cause.

If you see a long chain of "WARNING: Method definition ... overwritten"
followed by "ERROR: Method overwriting is not permitted during Module
precompilation" (often mentioning SciMLBase, DiffEqBase, or similar) —
this is a package VERSION CONFLICT, usually caused by installing
packages outside an activated local environment (see step 4 above), or
by a stray `Pkg.add("SciMLBase")` (not needed by this toolbox at all —
safe to remove with `Pkg.rm("SciMLBase")` if you ever installed it).
Fix:

    using Pkg
    Pkg.activate(".")     # make sure you're in THIS project's environment
    Pkg.rm("SciMLBase")   # only if it's listed and you added it manually
    Pkg.resolve()
    Pkg.instantiate()
    Pkg.precompile()      # forces a clean rebuild; can take several minutes

Then fully restart Julia and try again. If it still fails, closing
Julia and deleting the folder C:\Users\<you>\.julia\compiled\ forces
everything to recompile from scratch (slow, but reliably fixes stuck
cache issues).


PART 3: EDITING A SAVED PLOT (edit_plot.jl)
=======================================================

Re-draws a plot from an already-saved *_fit_object.jld2 file with
different fonts/sizes/colors — no re-fitting needed, so it's fast.

  1. Run run_example.jl first (need the .jld2 file to already exist).
  2. Open edit_plot.jl in a text editor, change settings under
     "PART 1: WHAT TO CHANGE" (jld2_path, font sizes, colors, sizes,
     export format).
  3. In the Julia REPL:

        include("edit_plot.jl")

  4. Look at the new file in output/ (named "..._edited.png" by default).
  5. Repeat: change a setting, re-run `include("edit_plot.jl")` — no
     need to close Julia or re-fit anything each time.


PART 4: PRACTICAL IDENTIFIABILITY SCREEN (Identifiability.jl)
=======================================================

Checks whether a growth model's free parameters (r, p, a, K) can be
told apart from the SHAPE of the data, before you spend time fitting.
A model can be "FLAGGED" here even if it later gives a good-looking fit
— that means the individual parameter values shouldn't be trusted
on their own (they may be trading off against each other), even though
the overall curve/forecast can still be fine. GRM is the model most
often flagged this way; GLM/LM/RICH are usually cleaner.

Run it on its own (doesn't need run_example.jl to have run first):

    include("Identifiability.jl")
    using .Identifiability
    using DelimitedFiles

    data = readdlm("jalisco.txt", '\t', Float64)
    timevect = data[1:40, 1]     # match your calibration_period
    ydata = data[1:40, 2]

    results = Identifiability.screen_all_models(timevect, ydata)

This prints a collinearity number for each model (< 20 = OK, higher =
FLAGGED) using a quick heuristic starting point — no optimizer/fitting
required, so it's cheap to run before committing to a full bootstrap.

To dig into ONE model in detail (e.g. to see exactly which pair of
parameters is trading off), call it directly with debug=true:

    params0 = (r=0.5, p=0.9, a=1.0, K=min(sum(ydata), 1e5), I0=ydata[1])
    result = Identifiability.screen_identifiability(:grm, timevect, params0;
                                                     debug=true)

This prints the full sensitivity matrix and every pairwise collinearity
value, so you can see exactly which parameters are (or aren't) the
problem. Note: if the model's FULL collinearity is much higher than its
worst PAIRWISE collinearity, that means the issue involves 3+ parameters
jointly, not just one pair — this is common for GRM specifically.

