% Generated by roxygen2: do not edit by hand
% Please edit documentation in R/dm.growth.fit.double.R
\name{dm.growth.fit.double}
\alias{dm.growth.fit.double}
\title{Fit bimodal dendrometer growth curves by vegetation season}
\usage{
dm.growth.fit.double(
  df,
  TreeNum = "all",
  method = c("gompertz", "richards"),
  years = "all",
  year_mode = c("yearly", "pooled"),
  fit_GRO = TRUE,
  site_mode = c("NH", "SH", "CS"),
  custom_veg_season = c(275, 274),
  growth_fraction = c(0.1, 0.9),
  min_unique_growth = 10,
  rate_threshold_fraction = 0.1,
  peak_separation_min = 10,
  valley_ratio_max = 0.4,
  min_relative_peak = 0.05,
  fallback_to_single = TRUE,
  fix_a_to_observed_max = FALSE,
  fixed_a_multiplier = 1,
  start_value_double_gompertz_parameters = list(a = NA_real_, k = NA_real_, t0 =
    NA_real_, a2 = NA_real_, k2 = NA_real_, t02 = NA_real_),
  start_value_double_richards_parameters = list(a = NA_real_, k = NA_real_, t0 =
    NA_real_, v = 1, a2 = NA_real_, k2 = NA_real_, t02 = NA_real_, v2 = 1),
  verbose = TRUE
)
}
\arguments{
\item{df}{A data frame or tibble. The first column must be a time stamp
\code{Date}, \code{POSIXct}, or parseable date-time string. Remaining
selected columns must be numeric dendrometer series.}

\item{TreeNum}{Either \code{"all"} to use all dendrometer series, a numeric
vector selecting dendrometer columns by position, or a character vector of
column names.}

\item{method}{Double-curve fitting method. One of \code{"gompertz"} or
\code{"richards"}.}

\item{years}{Either \code{"all"} to fit all available vegetation seasons, or
a character vector of season labels to retain.}

\item{year_mode}{Either \code{"yearly"} to fit one curve per vegetation
season, or \code{"pooled"} to fit one pooled curve across all retained
seasons.}

\item{fit_GRO}{Logical. If \code{TRUE}, processed daily series are converted
to cumulative growth using a cumulative maximum within each vegetation
season.}

\item{site_mode}{Vegetation season definition. One of \code{"NH"},
\code{"SH"}, or \code{"CS"}.}

\item{custom_veg_season}{Numeric vector of length two giving the start and
end day-of-year for custom vegetation seasons in \code{"CS"} mode.}

\item{growth_fraction}{Numeric vector of length two giving the lower and
upper fractions of final fitted seasonal growth used to define overall
growing-season onset and cessation.}

\item{min_unique_growth}{Minimum number of unique non-missing cumulative
growth values required before a fit is attempted.}

\item{rate_threshold_fraction}{Numeric scalar between 0 and 1. Pulse start
and end are defined as the first and last days where fitted growth rate
exceeds this fraction of the pulse-specific peak fitted growth rate.}

\item{peak_separation_min}{Minimum number of days separating the two fitted
derivative peaks required to classify a fit as truly two-pulse.}

\item{valley_ratio_max}{Maximum allowed ratio between the valley rate and the
weaker of the two derivative peaks. Smaller values require a deeper valley
between pulses.}

\item{min_relative_peak}{Minimum relative height, expressed as a fraction of
the global derivative maximum, for a local derivative peak to be considered.}

\item{fallback_to_single}{Logical. If \code{TRUE}, and the fitted double curve
does not show a convincing two-pulse pattern according to derivative-based
criteria, the function refits a corresponding single growth curve
\code{"gompertz"} or \code{"richards"} and returns that fit instead.}

\item{fix_a_to_observed_max}{Logical. If \code{TRUE}, the total asymptote of
the double-growth curve is fixed to the observed maximum cumulative growth
for that series and vegetation season. For double-Gompertz and
double-Richards models, this means \code{a + a2} is fixed, while the
relative contribution of the two pulses is estimated. This is most
appropriate with \code{year_mode = "yearly"}. For
\code{year_mode = "pooled"}, one pooled maximum is used.}

\item{fixed_a_multiplier}{Numeric multiplier applied to the observed maximum
when \code{fix_a_to_observed_max = TRUE}. The default is \code{1}, meaning
the total asymptote is fixed exactly to the observed seasonal maximum.
Values slightly above 1, for example \code{1.01} or \code{1.05}, allow the
fitted asymptote to sit slightly above the observed maximum.}

\item{start_value_double_gompertz_parameters}{Optional named list of starting
values for the double-Gompertz fit. Supported names are \code{a}, \code{k},
\code{t0}, \code{a2}, \code{k2}, and \code{t02}. If
\code{fix_a_to_observed_max = TRUE}, supplied \code{a} and \code{a2} are
used only to initialize the relative pulse contribution.}

\item{start_value_double_richards_parameters}{Optional named list of starting
values for the double-Richards fit. Supported names are \code{a}, \code{k},
\code{t0}, \code{v}, \code{a2}, \code{k2}, \code{t02}, and \code{v2}. If
\code{fix_a_to_observed_max = TRUE}, supplied \code{a} and \code{a2} are
used only to initialize the relative pulse contribution.}

\item{verbose}{Logical. If \code{TRUE}, prints a short completion message.}
}
\value{
An object of class \code{"dm_growth_fit"} with elements:
\describe{
  \item{call}{The matched function call.}
  \item{original_daily_data}{Raw daily dendrometer data after resampling to
    daily maxima and assigning vegetation seasons, but before centering and
    optional cumulative-growth transformation.}
  \item{processed_data}{Daily processed data used for fitting.}
  \item{fitted_data}{Daily fitted values on the full vegetation-season grid.}
  \item{fit_statistics}{Fit-level statistics and estimated timing.}
  \item{fit_parameters}{Fit-level model parameters and convergence
    information.}
  \item{season_table}{Vegetation seasons retained for fitting.}
}
}
\description{
Fits bimodal cumulative growth curves to daily dendrometer series using either
a double-Gompertz or double-Richards model.

Overall growing-season timing is derived from the fitted cumulative-growth
curve using \code{growth_fraction}. Pulse-specific timing is derived from the
derivative of the fitted curve using a pulse-specific relative rate threshold
given by \code{rate_threshold_fraction}.

Pulse-specific timing is returned as \code{NA} when the fitted curve does not
show a convincing two-pulse pattern according to derivative-based criteria.

If \code{fallback_to_single = TRUE} and no convincing two-pulse pattern is
found, the function refits a corresponding single growth curve and returns
that fit instead. In that case, overall season timing is still returned, but
pulse-specific timing remains \code{NA}.

For double-Gompertz and double-Richards fits, the total asymptote can
optionally be fixed to the observed maximum cumulative growth of the
corresponding series and vegetation season. In the double-curve case, this
means \code{a + a2} is fixed, while the relative contribution of the first
and second pulse is estimated.
}
\details{
The second pulse is constrained to occur after the first pulse, which improves
numerical stability and reduces label switching between the two components.

For double-Gompertz and double-Richards models, the total seasonal asymptote
is:

\deqn{a_{total} = a + a2}

When \code{fix_a_to_observed_max = TRUE}, the function fits:

\deqn{a + a2 = max(y_{obs}) \times fixed\_a\_multiplier}

where \eqn{y_{obs}} is the observed cumulative growth of the selected
dendrometer series and vegetation season.

The fitted pulse contributions are still returned as \code{a} and \code{a2},
and their sum should equal \code{fixed_a_value}, apart from small numerical
rounding.

Non-applicable parameters are returned as \code{NA}. For example, \code{b}
and \code{b2} are relevant for double-Gompertz, while \code{v} and \code{v2}
are relevant for double-Richards.
}
\examples{
\donttest{
# Double-Gompertz with total seasonal asymptote fixed
# fit_gomp <- dm.growth.fit.double(
#   df = dendro_data,
#   TreeNum = "all",
#   method = "gompertz",
#   year_mode = "yearly",
#   fit_GRO = TRUE,
#   fix_a_to_observed_max = TRUE
# )

# Double-Richards with the total asymptote set 2 percent above observed max
# fit_rich <- dm.growth.fit.double(
#   df = dendro_data,
#   TreeNum = "all",
#   method = "richards",
#   year_mode = "yearly",
#   fit_GRO = TRUE,
#   fix_a_to_observed_max = TRUE,
#   fixed_a_multiplier = 1.02
# )
}

}
\seealso{
[dm.growth.fit()], [summary.dm_growth_fit()],
  [print.dm_growth_fit()]
}
