---
title: "Working with fitted nlpsem models"
output: rmarkdown::html_vignette
vignette: >
  %\VignetteIndexEntry{Working with fitted nlpsem models}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r setup, include=FALSE}
knitr::opts_chunk$set(collapse = TRUE, comment = "#>", fig.width = 7, fig.height = 4)
library(nlpsem)
```

Newly fitted models retain the specification used to construct them. You can inspect
and plot the returned object without repeating its outcome names, curve family, or
selected waves. The underlying OpenMx model remains available in `fit@mxOutput`.

## Fit a small model

This example generates five measurements for 80 people with a random intercept and
slope. Times are measured from the first occasion. The example is deliberately small
so that it can be fitted when this article is built.

```{r fit-model, message=FALSE}
set.seed(20261024)
n <- 80L
times <- 0:4
intercept <- rnorm(n, mean = 10, sd = 2)
slope <- rnorm(n, mean = 1.5, sd = 0.3)
dat <- data.frame(ID = seq_len(n))
for (j in seq_along(times)) {
  dat[[paste0("T", j)]] <- times[j]
  dat[[paste0("Y", j)]] <- intercept + slope * times[j] + rnorm(n, sd = 0.5)
}

fit <- getLGCM(
  dat, t_var = "T", y_var = "Y", records = 1:5,
  curveFun = "LIN", intrinsic = FALSE, paramOut = TRUE
)
```

## Inspect the fitted object

The compact print method gives an overview. The summary method delegates to OpenMx,
so its convergence and fit diagnostics remain available.

```{r inspect-model}
print(fit)
summary(fit)
```

Optimizer completion does not establish statistical admissibility. Inspect stored
covariance matrices, uncertainty information and component sizes without refitting:

```{r fit-diagnostics}
diagnostics <- getDiagnostics(fit)
diagnostics
```

A materially negative variance or indefinite latent covariance requires investigation.
Small boundary eigenvalues need scale-aware interpretation. Missing Hessian or standard
error information means that check is unavailable. These checks do not establish global
optimality: repeat fitting with defensible alternative starts and compare admissible
solutions. Keep the seed, optimizer and package versions with the analysis.

The coefficient method returns the named free parameters at full precision.
`printTable(fit)` displays the package's estimate table, including derived quantities
when that model supplies them; these serve different purposes.

```{r estimates}
coef(fit)
logLik(fit)
printTable(fit)
```

Use `getSpecification()` to inspect the recorded modeling choices and fitting
provenance. This is useful when returning to a saved analysis or explaining how a
figure was constructed.

```{r specification}
provenance <- getSpecification(fit)
names(provenance)
provenance$specification
provenance$versions
```

The full object also retains resolved starting values, fitting settings, and available
RNG state. Save the original data together with the fitted object; provenance records
the specification but is not an automatically replayable call.

## Plot without repeating the specification

The plotting method reads the recorded model specification. Supply only presentation
choices such as the time label and outcome title.

```{r plot-model, message=FALSE, warning=FALSE, fig.alt="The simulated mean outcome increases from about 10 at baseline to 16 after four years. The fitted linear mean closely follows the smoothed observed mean, with pointwise 95 percent confidence limits on either side."}
plot(fit, xstarts = 0, xlab = "Years since baseline", outcome = "Simulated outcome")
```

The intervals describe uncertainty in the model-implied mean at each plotted time.
They are pointwise intervals, not prediction intervals for individual people or a
simultaneous confidence band for the entire curve.

For further customization, request the plotting object without drawing it:

```{r collect-plot}
figures <- plot(fit, draw = FALSE, xstarts = 0, xlab = "Years since baseline",
                outcome = "Simulated outcome")
class(figures)
```

Its `figures` slot contains the ggplot objects. The layout follows `getFigure()`:
one entry per outcome, with growth-status or change/rate plots within that entry.

## Check a model on simulated data

`getSimData()` generates wide-format data from a model with known parameter values.
Each data set carries those values as the attribute `truth`, retaining the generating
parameterization and time origin. This example generates six measurements for 500 people from a quadratic
latent growth curve model with the default parameter values.

```{r simulate-data, message=FALSE}
sim <- getSimData(n = 500, curveFun = "QUAD", nT = 6, seed = 20261027)
truth <- attr(sim, "truth")
sim_fit <- getLGCM(
  sim, t_var = "T", y_var = "Y", records = 1:6,
  curveFun = "QUAD", intrinsic = FALSE, paramOut = TRUE
)
estimates <- sim_fit@Estimates[match(names(truth), sim_fit@Estimates$Name), ]
data.frame(truth = truth, estimate = round(estimates$Estimate, 3), SE = round(estimates$SE, 3))
```

Equal parameter names do not always mean equal parameters. For example, a quadratic
growth curve's `eta0` is its level at time zero, while an LCSM's `eta0` is its level
at the first fitted occasion. For quadratic and nonparametric data, `truth_lcsm`
supplies the parameter targets at the first generated occasion:

```{r simulate-time-origin}
shifted <- getSimData(n = 30, curveFun = "QUAD", times = 5:14, jitter = 0, seed = 1)
cbind(LGCM = attr(shifted, "truth")[c("mueta0", "psi00")],
      LCSM = attr(shifted, "truth_lcsm")[c("mueta0", "psi00")])
attr(shifted, "truth_info")$first_observation_time
```

The quadratic transformation includes the mean, covariance and covariate effects;
the initial mean and variance above are 125 and 166.25 for the LCSM, versus the
time-zero values 50 and 25. The nonparametric generator already starts at the first
observation. These targets cover all generated records; a later selected baseline
needs reparameterization, potentially for each person if that time is jittered.
`truth_info` records the origin, reference records and applicable limits.

`truth_lcsm` is unavailable for EXP and JB: their midpoint approximation, and a
random ratio's nonlinear transformation, preclude treating a simple conversion
as an exact recovery target. Direct comparisons with the generating `truth` need
a zero first fitted time for every person and the approximation qualifications in
`?getSimData`. For TVC models, transformed factor targets alone do not establish
equivalence of the full growth-curve and change-score data-generating models.

The data also carry each person's growth factors (`growth_factors`) and the full
generating values (`settings`). Supply `params` to change the generating values, and
a list of parameter sets with `prop` to generate latent classes. The same function
generates data for multivariate, time-varying covariate and mediation models:

```{r simulate-families}
bivariate <- getSimData(n = 200, sub_Model = "MGM", curveFun = "LIN", seed = 1)
with_tvc <- getSimData(n = 200, sub_Model = "TVC", curveFun = "LIN", decompose = 1, seed = 1)
mediation <- getSimData(n = 200, sub_Model = "MED", curveFun = "LIN", x_type = "baseline", seed = 1)
head(attr(mediation, "truth"))
```

The matching fit for each is `getMGM()`, `getTVCmodel()` and `getMediation()` with the
same functional form; `?getSimData` lists the parameter values each model takes.

## Reuse existing analyses

The fitting functions retain their existing arguments and output slots. Calls that
work directly with OpenMx models, such as the following, remain available:

```{r existing-api}
getSummary(model_list = list(fit@mxOutput))
```

Objects saved by older nlpsem versions may not contain specification metadata. Their
OpenMx results can still be inspected, but structural choices must be supplied when
plotting. For example, for an older univariate linear fit:

```{r legacy-plot, eval=FALSE}
plot(
  old_fit, sub_Model = "LGCM", y_var = "Y", curveFun = "LIN",
  t_var = "T", records = 1:5, xstarts = 0,
  xlab = "Years since baseline", outcome = "Outcome"
)
```

Use the specification from the original analysis. Do not infer the selected waves or
curve family solely from the appearance of a plotted trajectory. See the model-specific
vignettes for time-varying covariates, multiple outcomes, mediation, groups, and mixtures.
