---
title: "User Specified Genome Wide Association Study"
output:
  rmarkdown::html_vignette:
    self_contained: false
vignette: >
  %\VignetteIndexEntry{Custom GWAS}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

```{r, include = FALSE}
knitr::opts_chunk$set(
  collapse = TRUE,
  comment = "#>"
)
is_CRAN <- !identical(Sys.getenv("NOT_CRAN"), "true")
knitr::opts_chunk$set(eval = !is_CRAN)
```

In some cases, a user may want to specify custom models  to test hypotheses that we (as developers) may not have thought of. One of the easiest ways to build these models is by using Onyx, to literally draw the path model, export the script that constructs the model, and run the GWAS. Alternatively, users can simply write their own GWAS model using the mxPath specification in OpenMx.  In this tutorial we will walk you through building the model with Onyx.


# Installing the necessary software

The first step is to download the Onyx software.  This software can be downloaded from the [Onyx](http://onyx.brandmaier.de) website. Onyx is written in Java, which users will also need to install if it is not installed already.


## Building a Custom GWAS Model

In this example, we will build a latent growth curve (LGC) GWAS model.  The LGC model is a common phenotypic model for developmental processes.  For example, imagine that you have repeated (longitudinal) measures of a  phenotype at regular intervals (say annually), as well as GWAS data on the same individuals. In the initial version of GW-SEM, we constructed a built-in function for analyzing this type of model, but the current release does not offer a built in LGC function. Here we demonstrate how to build a LGC GWAS model from scratch and perform a genome-wide association test on the slope.


# Designing the Model

After opening the Onyx software, you will be greeted with a blank screen.  To begin, right click on the screen and you will be prompted with a series of options. As we would like to create a new LGC model, choose **_Create new model > Create new LGCM_**. 

![First Step in Specifying the Onyx LGC Model](https://jpritikin.github.io/gwsem/gwsemCustomExample/basicModel.jpg)

This will prompt you to set up some basic parameter of the latent growth model, such as the number of time points, where you would like to center the growth curve parameters, etc.  In this example, we will use the following options: 

![Specifying Options for the LGC Model](https://jpritikin.github.io/gwsem/gwsemCustomExample/options.png)


This will produce the following figure: 

![The initial Figure from Onyx](https://jpritikin.github.io/gwsem/gwsemCustomExample/initialFig.jpg)

The next step is to add parameters to the model that we will need for the GWAS portion.  Specifically, we will add 1) a name to the model, 2) the SNP, 3) regressions from the latent intercept and latent slope variables onto the SNP, and 4) a covariance between the intercept and the slope.

To name the model, right click on the canvas white space and several options will appear. In the text box, change the name to LCG GWAS.

To add the SNP, right click on the white space and click **_Create Variable > Observed_**. Then right click on the new box that appears and change the Variable name to _**SNP**_.

![Adding the SNP to the Path Diagram](https://jpritikin.github.io/gwsem/gwsemCustomExample/addSNP.jpg)

To add regressions paths from the SNP to the intercept and slope variables, right click on the new SNP variable and choose **_Add Path > Add Regression_**. You will want to do this for the intercept and the slope.  Once the regression paths are in the diagram, right click on each and choose **_Free Parameter_**. This will tell the model to estimate values for these paths, which are the essential components for the GWAS. 

To further customize the model, it is possible to estimate a covariance between the intercept and slope by right clicking on the intercept, choosing **_Add Path > Add Covariance_**, and then freeing the covariance path as was done for the SNP regressions.

Finally, we want to give the model sensible starting values, which can be done by right clicking on the respective paths and changing the value from the default _**1.0**_ to a more sensible value. Better, but not perfect, starting values can increase the optimization speed.

Note that the loadings from the intercept to the manifests are all fixed to 1, and the loadings from slope to the manifests increase linearly, but are fixed at particular values (i.e. 0, 1, 2, 3). This is done as part of the LGC model.

At this point you have drawn a path diagram for a basic LGC GWAS model.  

![The Final Path Diagram from Onyx](https://jpritikin.github.io/gwsem/gwsemCustomExample/finalPathDiagram.jpg)



## Building the GWAS model in R

Once we have drawn the path model in Onyx, we can export the model syntax into a form that can be read by R, and customize it so that it can be read by GW-SEM. To export the model, right click on the white space and choose **_Show Script > OpenMx (Path)_**. This will bring up a window at the top of the path diagram with syntax that can be copied into an R script.

It is now necessary to edit the code to remove irrelevant syntax and make a few changes to accommodate covariates. To make these changes easier, we gather up all the mxPath statements into a list.

This is the R Script that was generated by Onyx with the modifications highlighted.
```{r}
location <- 'https://jpritikin.github.io/gwsem/gwsemCustomExample'

#
# This model specification was automatically generated by Onyx
# 
library("OpenMx")                                                       # Edited
modelData <- read.table(file.path(location, "lgcData.txt"), header = TRUE)                     # Cut
manifests<-c("time1","time2","time3","time4","snp")                      # Kept
latents<-c("intercept","slope")                                          # Kept
model <- mxModel("LGC_GWAS",                                             # Edited
type="RAM",                                                              # Cut
manifestVars = manifests,                                                # Cut
latentVars = latents,                                                    # Cut
mxPath(from="intercept",to=c("time1","time2","time3","time4"), free=c(FALSE,FALSE,FALSE,FALSE), value=c(1.0,1.0,1.0,1.0) , arrows=1, label=c("intercept__time1","intercept__time2","intercept__time3","intercept__time4") ),   # Edited
mxPath(from="slope",to=c("time2","time3","time4"), free=c(FALSE,FALSE,FALSE), value=c(1.0,2.0,3.0) , arrows=1, label=c("slope__time2","slope__time3","slope__time4") ),                                  # Edited
mxPath(from="one",to=c("intercept","slope"), free=c(TRUE,TRUE), value=c(1.0,1.0) , arrows=1, label=c("const__intercept","const__slope") ),                                             # Edited
mxPath(from="snp",to=c("intercept","slope"), free=c(TRUE,TRUE), value=c(0.5,0.1) , arrows=1, label=c("snp__intercept","snp__slope") ),                                                 # Edited
mxPath(from="intercept",to=c("intercept","slope"), free=c(TRUE,TRUE), value=c(1.0,0.1) , arrows=2, label=c("sigma_i","COV_intercept_slope") ),                                               # Edited 
mxPath(from="slope",to=c("slope"), free=c(TRUE), value=c(1.0) , arrows=2, label=c("sigma_s") ),    # Edited
mxPath(from="time1",to=c("time1"), free=c(TRUE), value=c(1.0) , arrows=2, label=c("res") ),        # Edited 
mxPath(from="time2",to=c("time2"), free=c(TRUE), value=c(1.0) , arrows=2, label=c("res") ),        # Edited  
mxPath(from="time3",to=c("time3"), free=c(TRUE), value=c(1.0) , arrows=2, label=c("res") ),        # Edited 
mxPath(from="time4",to=c("time4"), free=c(TRUE), value=c(1.0) , arrows=2, label=c("res") ),        # Edited 
mxPath(from="snp",to=c("snp"), free=c(TRUE), value=c(1.0) , arrows=2, label=c("VAR_snp") ),        # Edited 
mxPath(from="one",to=c("time1","time2","time3","time4","snp"), free=F, value=0, arrows=1),         # Edited 
mxData(modelData, type = "raw")                     # Cut
);                                                  # Cut

result <- mxRun(model)                              # Cut
summary(result)                                     # Cut

```

This is the edited R Script that we can modify for GW-SEM:

```{r}
# This model that we will build on for GW-SEM

library(gwsem)

# load the LGC phenotypic data
lgcData <- read.table(file.path(location, "lgcData.txt"), header = TRUE)

# Use the Onyx Mode to edit the necessary components for the GWAS model
manifests<-c("time1","time2","time3","time4","snp")
latents<-c("intercept","slope")

path <- list(mxPath(from="intercept",to=c("time1","time2","time3","time4"), free=c(FALSE,FALSE,FALSE,FALSE), value=c(1.0,1.0,1.0,1.0) , arrows=1, label=c("intercept__time1","intercept__time2","intercept__time3","intercept__time4") ),
             mxPath(from="slope",to=c("time2","time3","time4"), free=c(FALSE,FALSE,FALSE), value=c(1.0,2.0,3.0) , arrows=1, label=c("slope__time2","slope__time3","slope__time4") ),
             mxPath(from="one",to=c("intercept","slope"), free=c(TRUE,TRUE), value=c(1.0,1.0) , arrows=1, label=c("const__intercept","const__slope") ),
             mxPath(from="snp",to=c("intercept","slope"), free=c(TRUE,TRUE), value=c(0.5,0.1) , arrows=1, label=c("snp__intercept","snp__slope") ),
             mxPath(from="intercept",to=c("intercept","slope"), free=c(TRUE,TRUE), value=c(1.0,0.1) , arrows=2, label=c("sigma_i","COV_intercept_slope") ),
             mxPath(from="slope",to=c("slope"), free=c(TRUE), value=c(1.0) , arrows=2, label=c("sigma_s") ),
             mxPath(from="time1",to=c("time1"), free=c(TRUE), value=c(1.0) , arrows=2, label=c("res") ),
             mxPath(from="time2",to=c("time2"), free=c(TRUE), value=c(1.0) , arrows=2, label=c("res") ),
             mxPath(from="time3",to=c("time3"), free=c(TRUE), value=c(1.0) , arrows=2, label=c("res") ),
             mxPath(from="time4",to=c("time4"), free=c(TRUE), value=c(1.0) , arrows=2, label=c("res") ),
             mxPath(from="snp",to=c("snp"), free=c(TRUE), value=c(1.0) , arrows=2, label=c("VAR_snp") ),
             mxPath(from="one",to=c("time1","time2","time3","time4","snp"), free=F, value=0, arrows=1))

# Package the components into a single GW-SEM Model

lgcGWAS <- mxModel("lgc", type="RAM",
        manifestVars = manifests,
        latentVars = c(latents, paste0('pc', 1:5)),
        path,
        mxExpectationRAM(M="M"),
        mxFitFunctionWLS(allContinuousMethod="marginals"),
        mxData(observed=lgcData, type="raw", minVariance=0.1, warnNPDacov=FALSE))

# Add the necessary covariates
lgcGWAS <- setupExogenousCovariates(lgcGWAS, paste0('pc', 1:5), paste0('time',1:4))

# Make sure that the model you specified runs properly and gives sensible parameter estimates
LGCtest <- mxRun(lgcGWAS)
summary(LGCtest)

library(curl)
curl_download(file.path(location, 'example.pgen'),
              file.path(tempdir(),'example.pgen'))
curl_download(file.path(location, 'example.pvar'),
              file.path(tempdir(),'example.pvar'))

# Run the GWAS Model
GWAS(lgcGWAS,
     file.path(tempdir(), "example.pgen"),
     file.path(tempdir(), "lgc.log"))

```

The loadResults function calculates Z scores and P values for the `snp__slope` and `snp__int` parameters.
                                                                          
```{r}
LGCresInt <- loadResults(file.path(tempdir(), 'lgc.log'), 'snp__intercept')
LGCresSlo <- loadResults(file.path(tempdir(), 'lgc.log'), 'snp__slope')

```

In this example, we simulated snp1491 and snp1945 to be related to the latent intercept, and  snp1339 and snp1598 to be related to the latent slope, and snp1901 to be related to both the intercept and the slope. We can look at the top SNPs using the head and order function to see whether the simulated SNPs are the most predictive of the latent variables.


```{r}
head(LGCresInt[order(LGCresInt$Z, decreasing = T),])

head(LGCresSlo[order(abs(LGCresSlo$Z), decreasing = T),])

```

We can also present a Manhattan plot for the latent variable associations, or explore the data in other ways.
