Repository: silvxlabs/quicfire-tools (language: python)

Issue:
Update pre-commit action to run on PR (#97)



Discussion / hints:

Fix patch:
diff --git a/.github/workflows/build-docs.yml b/.github/workflows/build-docs.yml
index 5ad85f9..29cbd8d 100644
--- a/.github/workflows/build-docs.yml
+++ b/.github/workflows/build-docs.yml
@@ -13,5 +13,5 @@ jobs:
         with:
           python-version: 3.x
           cache: pip
-      - run: pip install mkdocs mkdocstrings mkdocstrings-python mkdocs-material
+      - run: pip install mkdocs mkdocstrings mkdocstrings-python mkdocs-material black
       - run: mkdocs gh-deploy --force --clean --verbose
diff --git a/.github/workflows/pre-commit.yaml b/.github/workflows/pre-commit.yaml
index a44706e..049fdc4 100644
--- a/.github/workflows/pre-commit.yaml
+++ b/.github/workflows/pre-commit.yaml
@@ -1,6 +1,8 @@
 name: Linting and Formatting
+
 on:
-  push:
+  pull_request:
+    branches: [ main ]
 
 jobs:
   pre-commit:
diff --git a/docs/explanation.md b/docs/explanation.md
new file mode 100644
index 0000000..6ab6aba
--- /dev/null
+++ b/docs/explanation.md
@@ -0,0 +1,79 @@
+## Inputs module
+
+The [`inputs`](reference.md#quicfire_tools.inputs) module in `quicfire_tools` is a central part of the package that handles the creation, modification,
+and reading of QUIC-Fire input file decks. It provides a programmatic interface to interact with the various input
+files required for a QUIC-Fire simulation.
+
+### SimulationInputs Class
+
+The [`SimulationInputs`](reference.md#quicfire_tools.inputs.SimulationInputs) class is the primary class in the
+`inputs` module.
+It represents a QUIC-Fire input file deck with attributes that represent the state of each input file in the deck.
+These attributes are instances of various [`InputFile`] subclasses, each corresponding to a specific input file.
+
+The `SimulationInputs` class provides several methods for creating and modifying simulations. For example, the
+`create_simulation` method is used to create a new simulation with default values for fuels, ignitions, and topography.
+To modify these default values, `SimulationInputs` offers `set_*` methods, such as `set_uniform_fuels` and
+`set_rectangle_ignition`, to modify common simulation parameters and maintain consistency across input files.
+
+The `write_inputs` method is also used to write all input files in the `SimulationInputs` object to a
+specified directory. This method translates the state stored in the `SimulationInputs` object into the
+text format used by QUIC-Fire input files. In addition, simulation state can be saved to a JSON file for sharing,
+version control, or modification by using the `to_json` method.
+
+The `SimulationInputs` class also provides methods for reading existing input file decks and converting
+`SimulationInputs` objects to and from JSON format. The `from_directory` method is used to initialize a
+`SimulationInputs` object from a directory containing a QUIC-Fire input file deck. The `to_json` and `from_json`
+methods are used to save and load `SimulationInputs` objects in JSON format.
+
+### InputFile Class
+
+Each input file in a QUIC-Fire input file deck has a unique [Pydantic](https://docs.pydantic.dev/latest/)
+Base Model class that represents the state of that input file. These classes are subclasses of the [`InputFile`]
+class, which provides common functionality for all input files.
+
+This common functionality includes methods for reading and writing input files to disk, as well as methods for
+converting input files to and from JSON format. The `InputFile` class also provides methods to output the documentation
+for a QUIC-Fire parameter in a Python dictionary format.
+
+#### InputFiles objects and Pydantic
+
+The `InputFile` class is a subclass of the Pydantic `BaseModel` class. Pydantic is a Python library that provides
+data validation and serialization for Python data structures. Pydantic is used in `quicfire_tools` to validate the
+state of input files and to serialize input files to and from JSON format.
+
+Valid data types, ranges, and defaults are provided for each input file's attributes in the Pydantic `BaseModel`
+subclass for that input file. This allows Pydantic to validate the state of input files and raise errors when
+invalid values are provided.
+
+## Outputs module
+
+The [`outputs`](reference.md#quicfire_tools.outputs)
+module in `quicfire-tools` is responsible for managing and processing simulation outputs produced by QUIC-Fire.
+It provides efficient ways to access, extract, and organize data from various output files, enabling further analysis
+and visualization of simulation results.
+
+### OutputFile class
+The [`OutputFile`](reference.md#quicfire_tools.outputs.OutputFile)
+class represents a single output variable from a QUIC-Fire simulation.
+It handles loading the data from disk into NumPy arrays for a given timestep.
+
+Key functionality includes:
+
+* Mapping compressed formats to dense NumPy arrays
+* Automatically detecting available timesteps
+* Extracting data for specific timesteps
+* Integration with SimulationOutputs class
+
+### SimulationOutputs class
+The [`SimulationOutputs`](reference.md#quicfire_tools.outputs.SimulationOutputs)
+class is the main interface for working with QUIC-Fire output data.
+It collects available outputs in an Outputs directory and constructs `OutputFile` instances for each output variable.
+
+Key functionality includes:
+
+* Detecting available output variables
+* Validating directory structure
+* Getting outputs as NumPy arrays
+* Writing outputs to Zarr files
+* Integration with Dask arrays
diff --git a/docs/how-to-guides.md b/docs/how-to-guides.md
new file mode 100644
index 0000000..44afc36
--- /dev/null
+++ b/docs/how-to-guides.md
@@ -0,0 +1,337 @@
+## Inputs
+
+QUIC-Fire is controlled by a deck of input files that specify fuel parameters, wind conditions, ignitions, topography, etc.
+The inputs module provides a simple interface to programatically create and modify new input file decks or read existing decks.
+The following guides provide step-by-step instructions for working with simple QUIC-Fire input decks.
+Please see [inputs](reference.md#quicfire_tools.inputs) for full documentation.
+
+### How to create a basic QUIC-Fire simulation
+
+QUIC-Fire input decks are created and modified using the [`SimulationInputs`](reference.md#quicfire_tools.inputs.SimulationInputs) class.
+
+```python
+from quicfire_tools.inputs import SimulationInputs
+```
+
+To create a simulation, use the [`create_simulation`](reference.md#quicfire_tools.inputs.SimulationInputs.create_simulation) method, and
+input basic information about the burn domain, initial weather conditions, and simulation parameters. In the following example, we are
+setting up a simulation that is 200x200 grid cells, with one vertical layer of fuels. The wind is blowing from the east at 1.7 m/s, and
+the simulation will run for 600 seconds of simulation time.
+
+```python
+simulation = SimulationInputs.create_simulation(
+    nx=200,
+    ny=200,
+    fire_nz=1,
+    wind_speed=1.7,
+    wind_direction=90,
+    simulation_time=600
+)
+```
+
+- **nx** and **ny** define the number of cells in the x and y axis of the simulation grid.
+- **fire_nz** determines the number of cells in the z-axis for the fire grid.
+- **wind_speed** and **wind_direction** set the initial wind conditions.
+- **simulation_time** specifies how long the simulation will run in seconds.
+
+This creates a basic input deck with default values for fuels, ignitions, and topography. This simulation can be run as-is, or modified using
+methods described below.
+
+### How to use set_* methods for common simulation parameters
+
+Once a simulation is created, it can be modified directly through methods in the `SimulationInputs` class.
+For common modifications, convenience methods starting with `set_*` are available. Guides for all `set_*` methods are below.
+
+#### How to set uniform fuel conditions
+
+To set and modify fuel parameters for uniform fuels, use the [`set_uniform_fuels`](reference.md#quicfire_tools.inputs.SimulationInputs.set_uniform_fuels) method.
+In the following example, we are setting surface fuel density to 0.7 kg/m^3, fuel moisture to 5% of its dry weight, and fuel height to 1 meter.
+
+```python
+simulation.set_uniform_fuels(
+    fuel_density=0.7,
+    fuel_moisture=0.10,
+    fuel_height=1.0
+)
+```
+
+- **fuel_density** sets the surface fuel density in kg/m^3.
+- **fuel_moisture** sets the surface fuel moisture content as a fraction of the fuel's dry weight.
+- **fuel_height** sets the surface fuel height in meters.
+
+#### How to set a rectangle ignition pattern
+
+By default, ignitions are set up perpendicular to the wind direction specified in `create_simulation`, spanning 80% of the domain
+edge length, 10% from either side. A different igntion line can be created using the [`set_rectangle_ignition`](reference.md#quicfire_tools.inputs.SimulationInputs.set_rectangle_ignition) method.
+In the following example, ignitions start 150m from the western edge of the domain (x=0) and 100m from the southern edge of the domain (y=0). The rectangle extends 10 meters east in the x-direction,
+and 100m north in the y-direction.
+
+```python
+simulation.set_rectangle_ignition(
+    x_min=150,
+    y_min=100,
+    x_length=10,
+    y_length=100
+)
+```
+
+- **x_min** and **y_min** set the coordinates of the bottom left corner of the ignition zone. These coordinates are specified in meters, not grid cells.
+- **x_length** and **y_length** set the length of the ignition zone in the x and y directions in meters.
+
+Ignition patterns other than rectangular can be specified using the [ignitions module](how-to-guides.md#define-ignitions-using-ignitionspy).
+Please see [ignitions](reference.md#quicfire_tools.ignitions) for a full list of available ignition patterns.
+
+#### How to specify which files to output
+
+Depending on the desired analyses, different files (.bin) may need to be output. To specify with files should be written, use the [`set_output_files`](reference.md#quicfire_tools.inputs.SimulationInputs.set_output_files) method and set the desired files to true. In the following example, we are specifying outputs for fuel density, various emissions, and wind grid components.
+
+```python
+simulation.set_output_files(
+    fuel_dens=True,
+    emissions=True,
+    qu_wind_inst=True
+)
+```
+
+- **fuel_dens** specifies a compressed array fuel density output.
+- **emissions** specifies CO, PM2.5, and water emissions outputs.
+- **qu_wind_inst** specifies gridded wind components (u, v, w) for the QUIC (wind) grid.
+
+By default, an unmodified simulation will output fuel density and QUIC winds. After using `set_output_files`, all output files not specified as `True` will be set to false.
+
+Please see the [`QUIC_fire`](reference.md#quicfire_tools.inputs.QUIC_fire) class for a full list of available output files.
+
+#### How to set custom fuels, ignitions, and topography from .dat files
+
+For more advanced simulations, it may be necessary to specify custom fuel parameters, ignition patterns, or topography using .dat files. These files must be created and provided by the user, but the [`set_custom_simulation`](reference.md#quicfire_tools.inputs.SimulationInputs.set_custom_simulation) method can be used to specify which custom .dat files should be used in the simulation.
+
+```python
+simulation.set_custom_simulation(
+    fuel = True,
+    ignition = True,
+    topo = True
+)
+```
+
+- **fuel** specifies that fuel density, moisture, and height are provided by treesrhof.dat, treesmoist.dat, and treesfueldepth.dat, respectively.
+- **ignition** specifies that ignitions are provided by ignite.dat
+- **topo** specifies that topography is provided by topo.dat
+
+Any parameter not set to `True` will not be specified by a custom .dat file.
+
+### How to directly modify input files
+
+Every QUIC-Fire input file is represented in an `InputFile` class in the [inputs](reference.md#quicfire_tools.inputs) module. These classes can be accessed as attributes of the `SimulationInputs` class, where their parameters can be modified. Some of the more commonly modified input files are below:
+
+- **QUIC_fire** contains parameters relating to the fire simulation, including the fire grid, fuels, ignitions, and output files.
+- **QU_TopoInputs** contains parameters relating to the underlying topography of the simulation, including topography type and smoothing parameters.
+- **Sensor1** contains parameters relating to the wind conditions throughout the simulation.
+- **Runtime_Adananced_User_Inputs** contains parameters relating to the internal processing of the simulation.
+
+#### QUIC_fire.inp
+
+The [`QUIC_fire`](reference.md#quicfire_tools.inputs.QUIC_fire) input file class contains many parameters relating to the fire simulation. Once a simulation is created, these parameters can be accessed and modified through [`SimulationInputs.quic_fire`](reference.md#quicfire_tools.inputs.SimulationInputs.quic_fire). In the following example, some parameters not accessed by the `set_*` methods are modified.
+
+```python
+simulation.quic_fire.random_seed = 47
+simulation.quic_fire.out_time_fire = 60
+simulation.quic_fire.ignitions_per_cell = 5
+simulation.quic_fire.auto_kill = 1
+```
+
+Please see [`QUIC_fire`](reference.md#quicfire_tools.inputs.QUIC_fire) for a full list of parameters associated with the QUIC_fire.inp input file.
+
+#### QU_TopoInputs.inp
+
+The [`QU_TopoInputs`](reference.md#quicfire_tools.inputs.QU_TopoInputs) input file class contains parameters relating to the underlying topography of the simulation. Once a simulation is created, these parameters can be accessed and modified throught [`QU_TopoInputs`](reference.md#quicfire_tools.inputs.SimulationInputs.qu_topoinputs). For information on setting custom topography using built-in methods in the [topography](reference.md#quicfire_tools.topography) module, see [Set custom topography](how-to-guides.md#define-topography-using-topographypy). In the following example, some parameters not accessed by the `set_*` methods or the `topography` module are modified.
+
+```python
+simulation.qu_topoinputs.smoothing_passes = 500
+simulation.qu_topoinputs.sor_iteration = 300
+```
+
+Please see [`QU_TopoInputs`](reference.md#quicfire_tools.inputs.QU_TopoInputs) for a full list of parameters associated with the QUIC_fire.inp input file.
+
+#### sensor1.inp
+The ['Sensor1](reference.md#quicfire_tools.inputs.Sensor1) input file class contains parameters defining wind conditions throughout the simulation. Once a simulation is created, these parameters can be accessed and modified through [`SimulationInputs.sensor1`](reference.md#quicfire_tools.inputs.SimulationInputs.sensor1).
+
+```python
+simulation.sensor1.sensor_height = 10
+```
+
+Please see [`Sensor1`](reference.md#quicfire_tools.inputs.Senor1) for a full list of parameters associated with the sensor1.inp input file.
+
+#### Runtime_Advanced_User_Inputs.inp
+
+The [`Runtime_Advanced_User_Inputs`](reference.md#quicfire_tools.inputs.Runtime_Advanced_User_Inputs) input file class condtains two parameters relating to the internal processing of the simulation. Once a simulation is created, these parameters can be accessed and modified throught [`Runtime_Advanced_User_Inputs`](reference.md#quicfire_tools.inputs.SimulationInputs.runtime_advanced_user_inputs). In the following example, the number of CPUs/threads is specifed, along with whether or not to use the adaptive computational window.
+
+```python
+simulation.runtime_advanced_user_inputs.num_cpus = 1
+simulation.runtime_advanced_user_inputs.use_acw = 1
+```
+
+### How to define topography using topography.py
+
+In addition to flat topography (the default) and [custom topography](how-to-guides.md#set-custom-fuels-ignitions-and-topography-from-dat-files), there are various built-in topography types, all of which can be set using classes in the [topopgraphy](reference.md#quicfire_tools.topography) module. The following example creates and sets Gaussian hill topography.
+
+```python
+from quicfire_tools.topography import GaussianHillTopo
+
+# First, create an TopoType class
+topo = GaussianHillTopo(
+    x_hilltop = 100,
+    y_hilltop = 150,
+    elevation_max = 50,
+    elevation_std = 15
+)
+
+# Next, assign it to the topo_type attribute of the qu_topoinputs InputFile
+simulation.qu_topoinputs.topo_type = topo
+```
+
+Please see [topopgraphy](reference.md#quicfire_tools.topography) for a full list of topography types.
+
+### How to define ignitions using ignitions.py
+
+In addition to [rectangle ignitions](how-to-guides.md#set-rectangle-ignition-patterns) (the default) and [custom ignitions](how-to-guides.md#set-custom-fuels-ignitions-and-topography-from-dat-files), there are various build-in ignition patterns, all of which can be set using class in the [ignnitions](reference.md#quicfire_tools.ignitions) module. The following example creates and sets a circular ring ignition.
+
+```python
+from quicfire_tools.ignitions import CurcularRingIgnition
+
+# First, create an IgnitionType class
+ignition = CircularRingIgnition(
+    x_min = 50,
+    y_min = 50,
+    x_length = 20,
+    y_length = 20,
+    ring_width = 10
+)
+
+# Next, assign it to the ignition_type attribute of the quic_fire InputFile
+simulation.quic_fire.ignition_type
+```
+
+Please see [igntions](reference.md#quicfire_tools.ignitions) for a full list of ignition patterns.
+
+### Set weather conditions
+
+What goes here?
+
+### How to read and write input file decks
+
+#### How to write a SimulationInputs object to an input deck
+
+Once a `SimulationInputs` object has been created and modified, it can be used to write all the necessary input files to a directory containing the QUIC-Fire executable.
+This is done using the [`write_inputs`](reference.md#quicfire_tools.inputs.SimulationInputs.write_inputs) method.
+
+```python
+simulation.write_inputs("path/to/directory")
+```
+
+#### How to load an existing input deck
+
+Input decks that already exist may be read in as a `SimulationInputs` object. The [`from_directory`](reference.md#quicfire_tools.inputs.SimulationInputs.from_directory) method
+is used for this purpose.
+
+```python
+simulation = SimulationInputs.from_directory("path/to/directory")
+```
+
+#### How to write a simulation to JSON
+
+All the information in a `SimulationInputs` object may be saved in JSON format using the [`to_json`](reference.md#quicire_tools.inputs.SimulationInputs.to_json) method.
+
+```python
+simulation.to_json("path/to/directory")
+```
+
+#### How to load a simulation from a JSON file
+
+Input decks saved in JSON format by quicfire-tools can be loaded into a `SimulationInputs` oject using the [`from_json`](reference.md#quicfire_tools.inputs.SimulationInputs.from_json) method.
+
+```python
+simulation = SimulationInputs.from_json("path/to/directory")
+```
+
+## Outputs
+
+### How to create a SimulationOutputs object from a directory containing QUIC-Fire output files
+
+To read and process QUIC-Fire output files, use the [`SimulationOutputs`](reference.md#quicfire_tools.outputs.SimulationOutputs) class.
+You need to specify the path to the directory containing the output files, as well as the number of cells in the z, y, and x directions of the simulation grid.
+
+```python
+from quicfire_tools.outputs import SimulationOutputs
+
+output_directory = "/path/to/output/directory"
+nz = 56  # number of z cells
+ny = 100  # number of y cells
+nx = 100  # number of x cells
+
+simulation_outputs = SimulationOutputs(output_directory, nz, ny, nx)
+```
+
+### How to get an OutputFile object from a SimulationOutputs object
+
+Once you have a `SimulationOutputs` object, you can use it to get an [`OutputFile`](reference.md#quicfire_tools.outputs.OutputFile) object for a specific output file.
+To do this, use the [`get_output_file`](reference.md#quicfire_tools.outputs.SimulationOutputs.get_output_file) method of the `SimulationOutputs` class.
+
+```python
+from quicfire_tools.outputs import SimulationOutputs
+simulation_outputs = SimulationOutputs("/path/to/output/directory", 56, 100, 100)
+
+output_name = "fire-energy_to_atmos"  # replace with the name of the output you are interested in
+output_file = simulation_outputs.get_output(output_name)
+```
+
+### How to get a numpy array from an OutputFile object at a specific timestep
+
+Once you have an `OutputFile` object, you can use it to get a numpy array for the output data using the
+[`to_numpy`](reference.md#quicfire_tools.outputs.OutputFile.to_numpy) method of the `OutputFile` instance. You can
+specify the timestep(s) you are interested in. If you don't provide a timestep, all timesteps will be returned:
+
+```python
+from quicfire_tools.outputs import SimulationOutputs
+simulation_outputs = SimulationOutputs("/path/to/output/directory", 56, 100, 100)
+output_file = simulation_outputs.get_output("fire-energy_to_atmos")
+
+timestep = 0  # replace with the timestep you are interested in
+output_data = output_file.to_numpy(timestep)
+```
+
+#### How to get the data across all timesteps
+
+By not specifying a timestep, you can get the data across all timesteps. This will return a 4D numpy array with the
+shape (nt, nz, ny, nx), where nt is the number of timesteps.
+
+_caution:_ please be aware of the memory requirements your data. This approach may not be appropriate for large simulations
+or for computers with limited memory.
+
+```python
+from quicfire_tools.outputs import SimulationOutputs
+simulation_outputs = SimulationOutputs("/path/to/output/directory", 56, 100, 100)
+output_file = simulation_outputs.get_output("fire-energy_to_atmos")
+
+output_data = output_file.to_numpy()
+```
+
+### How to get a dask array from a QUIC-Fire output file
+
+You can use the [`to_dask`](reference.md#quicfire_tools.outputs.SimulationOutputs.to_dask) method of the `SimulationOutputs` class to
+get a dask array for the desired output file. This method returns a
+[`dask.array`](https://docs.dask.org/en/latest/array.html) object, which can be used to read and process data in parallel,
+perform lazy computations, and work with data that is too large to fit in memory.
+
+Note that this method returns a dask array, not a numpy array. To get a numpy array, you can use the
+[`compute`](https://docs.dask.org/en/latest/array-api.html#dask.array.Array.compute) method of the dask array to perform
+the computations and load data into memory.
+
+The dask array returned by this method has the same format as the numpy array returned by the
+[`to_numpy`](reference.md#quicfire_tools.outputs.OutputFile.to_numpy) method, a 4D array with the shape
+(nt, nz, ny, nx), where nt is the number of timesteps.
+
+```python
+from quicfire_tools.outputs import SimulationOutputs
+simulation_outputs = SimulationOutputs("/path/to/output/directory", 56, 100, 100)
+dask_array = simulation_outputs.to_dask("fire-energy_to_atmos")
+```
diff --git a/docs/tutorials.md b/docs/tutorials.md
new file mode 100644
index 0000000..8eb0f8f
--- /dev/null
+++ b/docs/tutorials.md
@@ -0,0 +1,254 @@
+## Creating a QUIC-Fire Simulation with quicfire-tools.
+
+Welcome to the tutorial on how to create a QUIC-Fire simulation using quicfire-tools, a Python package designed to
+streamline the process of managing QUIC-Fire input file decks and processing output files. This tutorial will take you
+through the necessary steps to set up a basic QUIC-Fire simulation, focusing on using the SimulationInputs class from
+the inputs module.
+
+### Prerequisites
+
+Before starting this tutorial, ensure that you have:
+
+- Python 3.8 or higher installed on your system.
+- The quicfire-tools package installed on your system. If you do not have quicfire-tools installed, please see the
+  [installation instructions](index.md#installation) in the documentation.
+
+### Step 1: Import the SimulationInputs class
+
+Start by importing the SimulationInputs class from the quicfire_tools.inputs module:
+
+```python
+from quicfire_tools.inputs import SimulationInputs
+```
+
+### Step 2: Creating a Uniform Line Fire Simulation
+
+In this step, we will create a function to set up a basic QUIC-Fire simulation. This involves initializing the
+simulation, defining fuel characteristics, setting the ignition area, and specifying output files.
+
+#### Step 2.1: Initialize the simulation
+
+First, we initialize the simulation with the grid size, simulation time, and wind conditions. The
+[`create_simulation`](reference.md#quicfire_tools.inputs.SimulationInputs.create_simulation) method of
+[`SimulationInputs`](reference.md#quicfire_tools.inputs.SimulationInputs) is used for this purpose.
+
+```python
+simulation = SimulationInputs.create_simulation(
+    nx=200,
+    ny=200,
+    fire_nz=1,
+    wind_speed=1.7,
+    wind_direction=90,
+    simulation_time=600
+)
+```
+
+- **nx** and **ny** define the number of cells in the x and y axis of the simulation grid.
+- **fire_nz** determines the number of cells in the z-axis for the fire grid.
+- **wind_speed** and **wind_direction** set the initial wind conditions.
+- **simulation_time** specifies how long the simulation will run in seconds.
+
+#### Step 2.2: Define Uniform Fuel Characteristics
+
+Next, we define the characteristics of the fuel. The [`set_uniform_fuels`](reference.md#quicfire_tools.inputs.SimulationInputs.set_uniform_fuels)
+method sets the fuel density, moisture, and height uniformly across the simulation grid.
+
+```python
+simulation.set_uniform_fuels(
+    fuel_density=0.7,
+    fuel_moisture=0.10,
+    fuel_height=1.0
+)
+```
+
+- **fuel_density** sets the surface fuel density in kg/m^3.
+- **fuel_moisture** sets the surface fuel moisture content as a fraction of the fuel's dry weight.
+- **fuel_height** sets the surface fuel height in meters.
+
+#### Step 2.3: Set the Ignition Area
+
+Now, we specify the area where the fire will start. The [`set_rectangle_ignition`](reference.md#quicfire_tools.inputs.SimulationInputs.set_rectangle_ignition)
+method is used to create a rectangular ignition zone simulating a line fire ignition pattern.
+
+```python
+simulation.set_rectangle_ignition(
+    x_min=150,
+    y_min=100,
+    x_length=10,
+    y_length=100
+)
+```
+
+- **x_min** and **y_min** set the coordinates of the bottom left corner of the ignition zone. These coordinates are specified in meters, not grid cells.
+- **x_length** and **y_length** set the length of the ignition zone in the x and y directions in meters.
+
+#### Step 2.4: Specify Output Files
+
+Finally, we specify the types of output files the simulation will generate.
+The [`set_output_files`](reference.md#quicfire_tools.inputs.SimulationInputs.set_output_files)
+method allows you to choose which data to output.
+
+```python
+simulation.set_output_files(
+    fuel_dens=True,
+    emissions=True,
+    qu_wind_inst=True
+)
+```
+
+In this example:
+- **fuel_dens** specifies a compressed array fuel density output.
+- **emissions** specifies CO, PM2.5, and water emissions outputs.
+- **qu_wind_inst** specifies gridded wind components (u, v, w) for the QUIC (wind) grid.
+
+### Step 3: Write the Simulation to a Directory
+
+Now that we have created the simulation, we can write it to a directory containing a QUIC-Fire executable to run the
+fire model.
+The [`write_simulation`](reference.md#quicfire_tools.inputs.SimulationInputs.write_simulation)
+method of is used for this purpose.
+
+```python
+simulation.write_inputs("path/to/directory")
+```
+
+### Finished Code
+
+The following code block combines all the steps in this tutorial to create a basic QUIC-Fire simulation using
+quicfire-tools.
+
+```python
+from quicfire_tools.inputs import SimulationInputs
+
+simulation = SimulationInputs.create_simulation(
+    nx=200,
+    ny=200,
+    fire_nz=1,
+    wind_speed=1.7,
+    wind_direction=90,
+    simulation_time=600
+)
+
+simulation.set_uniform_fuels(
+    fuel_density=0.7,
+    fuel_moisture=0.10,
+    fuel_height=1.0
+)
+
+simulation.set_rectangle_ignition(
+    x_min=150,
+    y_min=100,
+    x_length=10,
+    y_length=100
+)
+
+simulation.set_output_files(
+    fuel_dens=True,
+    emissions=True,
+    qu_wind_inst=True
+)
+
+simulation.write_inputs("path/to/directory")
+```
+
+### Conclusion
+
+In this tutorial, we have learned how to create a basic QUIC-Fire simulation using the SimulationInputs class from the
+inputs module. We have also learned how to write the simulation to a directory containing a QUIC-Fire executable to run
+the fire model.
+
+quicfire-tools is designed to set up simulations in a quick, easy, and repeatable manner. The scope of the inputs module
+ends at writing the simulation to a directory. Running a simulation and understanding the relevant inputs are the
+responsibility of the user.
+
+For more information about setting up a QUIC-Fire simulation, please see the [How-to-Guide](how-to-guides.md)
+for more examples of working with the inputs module,
+and the [Reference](reference.md#quicfire_tools.inputs) page for a complete list of available methods and attributes.
+
+## Reading QUIC-Fire Output Files with quicfire-tools.
+
+In this tutorial, we will learn how to read QUIC-Fire output files using quicfire-tools, a Python package designed to
+streamline the process of managing QUIC-Fire input file decks and processing output files. This tutorial will take you
+through the necessary steps to read a QUIC-Fire output file into a numpy array, focusing on using the OutputFile class
+from the outputs module.
+
+### Prerequisites
+
+Before starting this tutorial, ensure that you have:
+
+- Python 3.8 or higher installed on your system.
+- The quicfire-tools package installed on your system. If you do not have quicfire-tools installed, please see the
+  [installation instructions](index.md#installation) in the documentation.
+- An Outputs directory containing the output files from a QUIC-Fire simulation. quicfire-tools does not handle running
+  QUIC-Fire simulations and it is the responsibility of the user to run the fire model and understand the relevant
+  inputs.
+
+### Step 1: Import the necessary modules
+
+Start by importing the SimulationInputs class from the quicfire_tools.outputs module:
+
+```python
+from quicfire_tools.outputs import SimulationOutputs
+```
+
+### Step 2: Create a SimulationOutputs object
+
+Next, create a SimulationOutputs object. The SimulationOutputs class is used to read and process QUIC-Fire outputs.
+You can create a SimulationOutputs object by passing the path to the Outputs directory containing the output files to
+the constructor.
+
+In addition to the path to the Outputs directory, you also need to specify the number of cells in the z, y, and x
+directions of the simulation grid. This information is used to reshape the output files into a 3D numpy array.
+
+```python
+output_directory = "/path/to/output/directory"
+nz = 56  # number of z cells
+ny = 100  # number of y cells
+nx = 100  # number of x cells
+
+simulation_outputs = SimulationOutputs(output_directory, nz, ny, nx)
+```
+
+### Step 3: Get an Output File
+
+You can use the SimulationOutputs object to get the `OutputFile` object for the output that you want to read data from.
+To do this, use the [`get_output_file`](reference.md#quicfire_tools.outputs.SimulationOutputs.get_output_file)
+method of the SimulationOutputs class.
+
+```python
+output_name = "fire-energy_to_atmos"  # replace with the name of the output you are interested in
+output_file = simulation_outputs.get_output(output_name)
+```
+
+### Step 4: Get the output data as a numpy array
+
+Finally, you can get a numpy array for the output data using the
+[`to_numpy`](reference.md#quicfire_tools.outputs.OutputFile.to_numpy) method of the `OutputFile` instance. You can
+specify the timestep(s) you are interested in. If you don't provide a timestep, all timesteps will be returned:
+
+```python
+timestep = 0  # replace with the timestep you are interested in
+output_data = output_file.to_numpy(timestep)
+```
+
+### Finished Code
+
+The following code block combines all the steps in this tutorial to read a QUIC-Fire output file into a numpy array
+using quicfire-tools.
+
+```python
+from quicfire_tools.outputs import SimulationOutputs
+
+output_directory = "/path/to/output/directory"
+nz = 56  # number of z cells
+ny = 100  # number of y cells
+nx = 100  # number of x cells
+
+simulation_outputs = SimulationOutputs(output_directory, nz, ny, nx)
+
+output_name = "fire-energy_to_atmos"  # replace with the name of the output you are interested in
+output_file = simulation_outputs.get_output(output_name)
+
+timestep = 0  # replace with the timestep you are interested in
+output_data = output_file.to_numpy(timestep)
+```
diff --git a/mkdocs.yml b/mkdocs.yml
index ffc98de..63e7ce7 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -3,12 +3,18 @@ site_name: quicfire-tools Documentation
 
 theme:
   name: "material"
+  features:
+    - content.code.copy
+
+markdown_extensions:
+  - pymdownx.highlight
+  - pymdownx.superfences
 
 nav:
   - Home: index.md
-  # - Explanation: explanation.md
-  # - Tutorials: tutorials.md
-  # - How-To Guides: how-to-guides.md
+  - Explanation: explanation.md
+  - Tutorials: tutorials.md
+  - How-To Guides: how-to-guides.md
   - Reference: reference.md
 
 repo_url: https://github.com/silvxlabs/quicfire-tools
diff --git a/quicfire_tools/data/documentation/QUIC_fire.inp.json b/quicfire_tools/data/documentation/QUIC_fire.inp.json
index 16d23eb..4941d15 100644
--- a/quicfire_tools/data/documentation/QUIC_fire.inp.json
+++ b/quicfire_tools/data/documentation/QUIC_fire.inp.json
@@ -148,12 +148,12 @@
         "can_be_modified": "Y"
     },
     "ignition_flag": {
-        "values_accepted": "[1 6]",
-        "tested_values_range": "1-6",
-        "description": "Ignition source shape flag: 1 = rectangle; 2 = square ring; 3 = circular ring; 4 = initial ignition locations provided through file QF_Ignitions.inp; 5 = time- and space-dependent ignition locations provided through file QF_IgnitionPattern.inp; 6 = ignite.dat (Firetec file); (NOTE: Flag of 7 seems to work better for ignite.dat - NJT)",
+        "values_accepted": "[1 7]",
+        "tested_values_range": "1-7",
+        "description": "Ignition source shape flag: 1 = rectangle; 2 = square ring; 3 = circular ring; 4 = initial ignition locations provided through file QF_Ignitions.inp; 5 = time- and space-dependent ignition locations provided through file QF_IgnitionPattern.inp; 6 = ignite.dat (Firetec file); (NOTE: Flag of 7 works better for ignite.dat and is the default for custom ignitions)",
         "units": "[-]",
         "can_be_modified": "Y",
-        "recommended_value": "6 (NJT recommendation: 7)"
+        "recommended_value": "1"
     },
     "ignitions_per_cell": {
         "values_accepted": "> 0",
@@ -161,7 +161,7 @@
         "description": "Number of ignition per cell of the fire model",
         "units": "[-]",
         "can_be_modified": "Y",
-        "recommended_value": "100 (seems to have no effect). (CHANGED RECOMMENDATION PER QF MEETINGS: 2)"
+        "recommended_value": "2"
     },
     "firebrand_flag": {
         "values_accepted": "[0 1]",
diff --git a/quicfire_tools/ignitions.py b/quicfire_tools/ignitions.py
index 3172cc0..6966b1c 100644
--- a/quicfire_tools/ignitions.py
+++ b/quicfire_tools/ignitions.py
@@ -11,15 +11,20 @@
 
 
 class IgnitionSources(int, Enum):
+    """
+    Enum class for all valid ignition source options in QUIC-Fire.
+    """
+
     rectangle = 1
     square_ring = 2
     circular_ring = 3
-    ignite_dat_file = 6
+    ignite_dat_file = 7
 
 
 class IgnitionType(BaseModel):
     """
-    Test docs
+    Base class for all ignition types in QUIC-Fire. This class is used to
+    provide a common string representation for all ignition types.
     """
 
     ignition_flag: IgnitionSources
@@ -30,7 +35,7 @@ def __str__(self):
             f"2 = square ring, 3 = circular ring, "
             f"4 = file (QF_Ignitions.inp), "
             f"5 = time-dependent ignitions (QF_IgnitionPattern.inp), "
-            f"6 = ignite.dat (firetech)"
+            f"7 = ignite.dat (firetech)"
         )
 
 
@@ -38,7 +43,7 @@ class RectangleIgnition(IgnitionType):
     """
     Represents a rectangle ignition source in QUIC-Fire.
 
-    Parameters
+    Attributes
     ----------
     x_min : float
         South-west corner in the x-direction [m]
@@ -51,10 +56,10 @@ class RectangleIgnition(IgnitionType):
     """
 
     ignition_flag: IgnitionSources = IgnitionSources(1)
-    x_min: int
-    y_min: int
-    x_length: int
-    y_length: int
+    x_min: float
+    y_min: float
+    x_length: float
+    y_length: float
 
     def __str__(self):
         flag_line = super().__str__()
@@ -71,7 +76,7 @@ class SquareRingIgnition(IgnitionType):
     """
     Represents a square ring ignition source in QUIC-Fire.
 
-    Parameters
+    Attributes
     ----------
     x_min : float
         South-west corner in the x-direction [m]
@@ -88,12 +93,12 @@ class SquareRingIgnition(IgnitionType):
     """
 
     ignition_flag: IgnitionSources = IgnitionSources(2)
-    x_min: int
-    y_min: int
-    x_length: int
-    y_length: int
-    x_width: int
-    y_width: int
+    x_min: float
+    y_min: float
+    x_length: float
+    y_length: float
+    x_width: float
+    y_width: float
 
     def __str__(self):
         flag_line = super().__str__()
@@ -112,7 +117,7 @@ class CircularRingIgnition(IgnitionType):
     """
     Represents a circular ring ignition source in QUIC-Fire.
 
-    Parameters
+    Attributes
     ----------
     x_min : float
         South-west corner in the x-direction [m]
@@ -127,11 +132,11 @@ class CircularRingIgnition(IgnitionType):
     """
 
     ignition_flag: IgnitionSources = IgnitionSources(3)
-    x_min: int
-    y_min: int
-    x_length: int
-    y_length: int
-    ring_width: int
+    x_min: float
+    y_min: float
+    x_length: float
+    y_length: float
+    ring_width: float
 
     def __str__(self):
         flag_line = super().__str__()
diff --git a/quicfire_tools/inputs.py b/quicfire_tools/inputs.py
index c87930d..2386e83 100644
--- a/quicfire_tools/inputs.py
+++ b/quicfire_tools/inputs.py
@@ -60,11 +60,9 @@
 
 class SimulationInputs:
     """
-    Class representing a QUIC-Fire input file deck.
-
-    This is the fundamental class in the quicfire_tools.data module. It is
-    used to create, modify, and write QUIC-Fire input file decks. It is also
-    used to read in existing QUIC-Fire input file decks.
+    Class representing a QUIC-Fire input file deck. This class is the primary
+    interface for building a QUIC-Fire input file deck and saving the input
+    files to a directory for running a simulation.
 
     Attributes
     ----------
@@ -163,32 +161,40 @@ def create_simulation(
         wind_speed: float,
         wind_direction: int,
         simulation_time: int,
-    ):
+    ) -> SimulationInputs:
         """
-        Creates a SimulationInputs object to build a QUIC-Fire input file deck
-        and run a simulation.
+        Creates a SimulationInputs object by taking in the mimum required
+        information to build a QUIC-Fire input file deck. Returns a
+        SimulationInputs object representing the complete state of the
+        QUIC-Fire simulation.
 
         Parameters
         ----------
         nx: int
-            Number of cells in the x-direction [-]
+            Number of cells in the x-direction [-]. Default cell size is 2m.
         ny: int
-            Number of cells in the y-direction [-]
+            Number of cells in the y-direction [-]. Default cell size is 2m.
         fire_nz: int
-            Number of cells in the z-direction for the fire grid [-]
+            Number of cells in the z-direction for the fire grid [-]. Default
+            cell size is 1m.
         wind_speed: float
-            Wind speed [m/s]
+            Wind speed [m/s].
         wind_direction: float
             Wind direction [deg]. 0 deg is north, 90 deg is east, etc. Must
             be in range [0, 360).
         simulation_time: int
-            Number of seconds to run the simulation for [s]
+            Number of seconds to run the simulation for [s].
 
         Returns
         -------
         SimulationInputs
-            Class containing the data to build a QUIC-Fire
-            input file deck and run a simulation using default parameters.
+            Class containing the data to build a QUIC-Fire input file deck and
+            run a simulation using default parameters.
+
+        Examples
+        --------
+        >>> from quicfire_tools import SimulationInputs
+        >>> sim_inputs = SimulationInputs.create_simulation(nx=100, ny=100, fire_nz=26, wind_speed=1.8, wind_direction=90, simulation_time=600)
         """
         # Initialize default input files
         rasterorigin = RasterOrigin()
@@ -241,7 +247,10 @@ def create_simulation(
     def from_directory(cls, directory: str | Path) -> SimulationInputs:
         """
         Initializes a SimulationInputs object from a directory containing a
-        QUIC-Fire input file deck.
+        QUIC-Fire input file deck. The function looks for each input file in the
+        QUIC-Fire input file deck, reads in the file to an object, and compiles
+        the objects to a SimulationInputs object that represents the complete
+        state of the QUIC-Fire simulation.
 
         Parameters
         ----------
@@ -252,6 +261,12 @@ def from_directory(cls, directory: str | Path) -> SimulationInputs:
         -------
         SimulationInputs
             Class containing the input files in the QUIC-Fire input file deck.
+
+        Examples
+        --------
+        >>> from quicfire_tools import SimulationInputs
+        >>> simulation_path = "path/to/simulation/directory"
+        >>> sim_inputs = SimulationInputs.from_directory(simulation_path)
         """
         if isinstance(directory, str):
             directory = Path(directory)
@@ -280,13 +295,20 @@ def from_directory(cls, directory: str | Path) -> SimulationInputs:
     @classmethod
     def from_dict(cls, data: dict) -> SimulationInputs:
         """
-        Initializes a SimulationInputs object from a dictionary containing
-        input file data.
+        Initializes a SimulationInputs object from a dictionary.
 
         Parameters
         ----------
         data: dict
             Dictionary containing input file data.
+
+        Examples
+        --------
+        >>> from quicfire_tools import SimulationInputs
+        >>> json_path = "path/to/json/object"
+        >>> sim_inputs = SimulationInputs.from_json(json_path)
+        >>> sim_dict = sim_inputs.to_dict()
+        >>> new_sim_inputs = SimulationInputs.from_dict(sim_dict)
         """
         return cls(
             rasterorigin=RasterOrigin.from_dict(data["rasterorigin"]),
@@ -323,6 +345,12 @@ def from_json(cls, path: str | Path) -> SimulationInputs:
         ----------
         path: str | Path
             Path to the JSON file.
+
+        Examples
+        --------
+        >>> from quicfire_tools import SimulationInputs
+        >>> json_path = "path/to/json/object"
+        >>> sim_inputs = SimulationInputs.from_json(json_path)
         """
         if isinstance(path, str):
             path = Path(path)
@@ -335,7 +363,7 @@ def write_inputs(self, directory: str | Path, version: str = "latest") -> None:
         Write all input files in the SimulationInputs object to a specified
         directory.
 
-        This method is the core method of the SimulationInputs class. It
+        This method is a core method of the SimulationInputs class. It
         is the principle way to translate a SimulationInputs object into a
         QUIC-Fire input file deck.
 
@@ -345,6 +373,12 @@ def write_inputs(self, directory: str | Path, version: str = "latest") -> None:
             Directory to write the input files to.
         version: str
             Version of the input files to write. Default is "latest".
+
+        Examples
+        --------
+        >>> from quicfire_tools import SimulationInputs
+        >>> sim_inputs = SimulationInputs.create_simulation(nx=100, ny=100, fire_nz=26, wind_speed=1.8, wind_direction=90, simulation_time=600)
+        >>> sim_inputs.write_inputs("path/to/simulation/directory")
         """
         if isinstance(directory, str):
             directory = Path(directory)
@@ -372,25 +406,37 @@ def write_inputs(self, directory: str | Path, version: str = "latest") -> None:
 
     def to_dict(self) -> dict:
         """
-        Convert the object to a dictionary representation. The SimulationInputs
-        object is represented as a nest dictionary, with the name of each
-        input file as a key to that input file's dictionary representation.
+        Convert the state of the SimulationInputs object to a dictionary.
+        The name of each input file in the SimulationInputs object is a key
+        to that input file's dictionary form.
 
-        Returns:
-        --------
+        Returns
+        -------
         dict
             Dictionary representation of the object.
+
+        Examples
+        --------
+        >>> from quicfire_tools import SimulationInputs
+        >>> sim_inputs = SimulationInputs.create_simulation(nx=100, ny=100, fire_nz=26, wind_speed=1.8, wind_direction=90, simulation_time=600)
+        >>> sim_dict = sim_inputs.to_dict()
         """
         return {key: value.to_dict() for key, value in self._input_files_dict.items()}
 
-    def to_json(self, path: str | Path):
+    def to_json(self, path: str | Path) -> None:
         """
-        Write the object to a JSON file.
+        Write the SimulationInputs object to a JSON file.
 
         Parameters
         ----------
         path : str | Path
             Path to write the JSON file to.
+
+        Examples
+        --------
+        >>> from quicfire_tools import SimulationInputs
+        >>> sim_inputs = SimulationInputs.create_simulation(nx=100, ny=100, fire_nz=26, wind_speed=1.8, wind_direction=90, simulation_time=600)
+        >>> sim_inputs.to_json("path/to/json/object")
         """
         if isinstance(path, str):
             path = Path(path)
@@ -402,7 +448,34 @@ def set_custom_simulation(
         fuel: bool = True,
         ignition: bool = True,
         topo: bool = True,
-    ):
+    ) -> None:
+        """
+        Sets the simulation to use custom fuel, ignition, and topography
+        settings.
+
+        This function can be useful for setting up simulations that use .dat
+        files to define custom fuel, topography, or ignition inputs.
+
+        Parameters
+        ----------
+        fuel : bool, optional
+            If True, sets the simulation to use custom fuel settings
+            (fuel flag 3). Default is True.
+        ignition : bool, optional
+            If True, sets the simulation to use a custom ignition source
+            (ignition flag 6). Default is True.
+        topo : bool, optional
+            If True, sets the simulation to use custom topography settings
+            (topography flag 5). Default is True.
+
+        Examples
+        --------
+        >>> from quicfire_tools import SimulationInputs
+        >>> sim_inputs = SimulationInputs.create_simulation(nx=100, ny=100, fire_nz=26, wind_speed=1.8, wind_direction=90, simulation_time=600)
+        >>> sim_inputs.set_custom_simulation(fuel=True, ignition=True, topo=True)
+        >>> sim_inputs.quic_fire.fuel_flag
+        3
+        """
         if fuel:
             self.quic_fire.fuel_flag = 3
             self.quic_fire.fuel_density = None
@@ -410,7 +483,7 @@ def set_custom_simulation(
             self.quic_fire.fuel_height = None
         if ignition:
             self.quic_fire.ignition_type = IgnitionType(
-                ignition_flag=IgnitionSources(6)
+                ignition_flag=IgnitionSources(7)
             )
         if topo:
             self.qu_topoinputs.topo_type = TopoType(topo_flag=TopoSources(5))
@@ -421,14 +494,61 @@ def set_uniform_fuels(
         fuel_moisture: float,
         fuel_height: float,
     ):
+        """
+        Sets the simulation to use uniform fuel settings. This function updates
+        the fuel flag to 1 and sets the fuel density, fuel moisture, and fuel
+        height to the specified values.
+
+        Parameters
+        ----------
+        fuel_density: float
+            Fuel bulk density [kg/m^3]. Note: This is the fuel bulk density, so
+            the fuel load should be normalized by the height of the fuel bed.
+        fuel_moisture: float
+            Fuel moisture content [%].
+        fuel_height: float
+            Fuel bed height [m].
+
+        Examples
+        --------
+        >>> from quicfire_tools import SimulationInputs
+        >>> sim_inputs = SimulationInputs.create_simulation(nx=100, ny=100, fire_nz=26, wind_speed=1.8, wind_direction=90, simulation_time=600)
+        >>> sim_inputs.set_uniform_fuels(fuel_density=0.5, fuel_moisture=25, fuel_height=1)
+        >>> sim_inputs.quic_fire.fuel_flag
+        1
+        >>> sim_inputs.quic_fire.fuel_density
+        0.5
+        """
         self.quic_fire.fuel_flag = 1
         self.quic_fire.fuel_density = fuel_density
         self.quic_fire.fuel_moisture = fuel_moisture
         self.quic_fire.fuel_height = fuel_height
 
     def set_rectangle_ignition(
-        self, x_min: int, y_min: int, x_length: int, y_length: int
-    ):
+        self, x_min: float, y_min: float, x_length: float, y_length: float
+    ) -> None:
+        """
+        Sets the simulation to use a rectangle ignition source. This function
+        updates the ignition flag to 1 and sets the ignition source to the
+        specified rectangle.
+
+        Parameters
+        ----------
+        x_min: float
+            South-west corner in the x-direction [m]
+        y_min: float
+            South-west corner in the y-direction [m]
+        x_length: float
+            Length in the x-direction [m]
+        y_length: float
+            Length in the y-direction [m]
+
+        Examples
+        -------
+        >>> from quicfire_tools import SimulationInputs
+        >>> sim_inputs = SimulationInputs.create_simulation(nx=100, ny=100, fire_nz=26, wind_speed=1.8, wind_direction=90, simulation_time=600)
+        >>> sim_inputs.set_rectangle_ignition(x_min=0, y_min=0, x_length=10, y_length=10)
+        """
         ignition = RectangleIgnition(
             x_min=x_min, y_min=y_min, x_length=x_length, y_length=y_length
         )
@@ -446,8 +566,49 @@ def set_output_files(
         mass_burnt: bool = False,
         emissions: bool = False,
         radiation: bool = False,
+        surf_eng: bool = False,
         intensity: bool = False,
-    ):
+    ) -> None:
+        """
+        Sets the simulation to output the specified files. Files set to True
+        will be output by the simulation, and files set to False will not be
+        output.
+
+        Parameters
+        ----------
+        eng_to_atm: bool, optional
+            If True, output the fire-energy_to_atmos.bin file. Default is False.
+        react_rate: bool, optional
+            If True, output the fire-reaction_rate.bin file. Default is False.
+        fuel_dens: bool, optional
+            If True, output the fuels-dens.bin file. Default is False.
+        qf_wind: bool, optional
+            If True, output the windu, windv, and windw .bin files.
+            Default is False.
+        qu_wind_inst: bool, optional
+            If True, output the quic_wind_inst.bin file. Default is False.
+        qu_wind_avg: bool, optional
+            If True, output the quic_wind_avg.bin file. Default is False.
+        fuel_moist: bool, optional
+            If True, output the fuels-moist.bin file. Default is False.
+        mass_burnt: bool, optional
+            If True, output the mburnt_integ.bin file. Default is False.
+        emissions: bool, optional
+            If True, output the co-emissions and pm-emissions .bin files.
+            Default is False.
+        radiation: bool, optional
+            If True, output the thermaldose and thermalradiation .bin files.
+            Default is False.
+        intensity: bool, optional
+            If True, output the fire-intensity.bin file. Default is False.
+
+        Examples
+        --------
+        >>> from quicfire_tools import SimulationInputs
+        >>> sim_inputs = SimulationInputs.create_simulation(nx=100, ny=100, fire_nz=26, wind_speed=1.8, wind_direction=90, simulation_time=600)
+        >>> sim_inputs.set_output_files(fuel_dens=True, mass_burnt=True)
+        """
+
         self.quic_fire.eng_to_atm_out = int(eng_to_atm)
         self.quic_fire.react_rate_out = int(react_rate)
         self.quic_fire.fuel_dens_out = int(fuel_dens)
@@ -457,7 +618,7 @@ def set_output_files(
         self.quic_fire.fuel_moist_out = int(fuel_moist)
         self.quic_fire.mass_burnt_out = int(mass_burnt)
         self.quic_fire.radiation_out = int(radiation)
-        self.quic_fire.intensity_out = int(intensity)
+        self.quic_fire.surf_eng_out = int(surf_eng)
         self.quic_fire.emissions_out = 2 if emissions else 0
 
     def _update_shared_attributes(self):
@@ -490,43 +651,42 @@ class InputFile(BaseModel, validate_assignment=True):
 
     This base class provides a common interface for all input files in order to
     accomplish two main goals:
+
     1) Return documentation for each parameter in the input file.
-    2) Provide a method to write the input file to a specified directory.
+
+    2) Provide a method to write the input file to a directory.
     """
 
     name: str
     _extension: str
-    _param_info: dict = None
 
     @property
     def _filename(self):
         return f"{self.name}{self._extension}"
 
     @property
-    def param_info(self):
+    def documentation_dict(self) -> dict:
+        # Return the documentation dictionary
+        with open(DOCS_PATH / f"{self._filename}.json", "r") as f:
+            return json.load(f)
+
+    def list_parameters(self) -> list[str]:
         """
-        Return a dictionary of parameter information for the input file.
+        Get a list of the names of all parameters in the input file.
         """
-        if self._param_info is None:  # open the file if it hasn't been read in
-            with open(DOCS_PATH / f"{self._filename}.json", "r") as f:
-                self._param_info = json.load(f)
-        return self._param_info
-
-    def list_parameters(self):
-        """List all parameters in the input file."""
-        return list(self.param_info.keys())
+        return list(self.documentation_dict.keys())
 
-    def get_documentation(self, parameter: str = None):
+    def get_documentation(self, parameter: str = None) -> dict:
         """
         Retrieve documentation for a parameter. If no parameter is specified,
         return documentation for all parameters.
         """
         if parameter:
-            return self.param_info.get(parameter, {})
+            return self.documentation_dict.get(parameter, {})
         else:
-            return self.param_info
+            return self.documentation_dict
 
-    def print_documentation(self, parameter: str = None):
+    def print_documentation_table(self, parameter: str = None) -> None:
         """
         Print documentation for a parameter. If no parameter is specified,
         print documentation for all parameters.
@@ -539,7 +699,7 @@ def print_documentation(self, parameter: str = None):
             key = key.replace("_", " ").capitalize()
             print(f"- {key}: {value}")
 
-    def to_dict(self, include_private: bool = False):
+    def to_dict(self, include_private: bool = False) -> dict:
         """
         Convert the object to a dictionary, excluding attributes that start
         with an underscore.
@@ -550,7 +710,7 @@ def to_dict(self, include_private: bool = False):
             Dictionary representation of the object.
         """
         all_fields = self.model_dump(
-            exclude={"name", "_extension", "_filename", "param_info"}
+            exclude={"name", "_extension", "_filename", "documentation_dict"}
         )
         if include_private:
             return all_fields
@@ -772,6 +932,8 @@ class QU_Simparams(InputFile):
     Class representing the QU_simparams.inp file. This file contains the
     simulation parameters for the QUIC-Fire simulation.
 
+    Attributes
+    ----------
     nx : int
         Number of cells in the x-direction [-]. Recommended value: > 100
     ny : int
@@ -1239,7 +1401,7 @@ class QUIC_fire(InputFile):
     contains the parameters relating to the fire simulation and
     outputs.
 
-    Parameters
+    Attributes
     ----------
     fire_flag : Literal[0, 1]
         Fire flag, 1 = run fire; 0 = no fire
@@ -1368,15 +1530,15 @@ class QUIC_fire(InputFile):
     eng_to_atm_out: Literal[0, 1] = 0
     react_rate_out: Literal[0, 1] = 0
     fuel_dens_out: Literal[0, 1] = 1
-    qf_wind_out: Literal[0, 1] = 1
-    qu_wind_inst_out: Literal[0, 1] = 0
+    qf_wind_out: Literal[0, 1] = 0
+    qu_wind_inst_out: Literal[0, 1] = 1
     qu_wind_avg_out: Literal[0, 1] = 0
     fuel_moist_out: Literal[0, 1] = 0
     mass_burnt_out: Literal[0, 1] = 0
     firebrand_out: Literal[0, 1] = 0
     emissions_out: Literal[0, 1, 2, 3, 4, 5] = 0
     radiation_out: Literal[0, 1] = 0
-    intensity_out: Literal[0, 1] = 0
+    surf_eng_out: Literal[0, 1] = 0
 
     @field_validator("random_seed")
     @classmethod
@@ -1532,7 +1694,8 @@ def from_file(cls, directory: str | Path):
         ignition_params = []
         current_line += 1
         for i in range(current_line, current_line + add):
-            ignition_params.append(int(lines[i].strip().split("!")[0]))
+            ignition_line = float(lines[i].split("!")[0].strip())
+            ignition_params.append(ignition_line)
         if ignition_flag == 1:
             x_min, y_min, x_length, y_length = ignition_params
             ignition_type = RectangleIgnition(
@@ -1557,8 +1720,6 @@ def from_file(cls, directory: str | Path):
                 y_length=y_length,
                 ring_width=ring_width,
             )
-        elif ignition_flag == 6:
-            ignition_type = IgnitionType(ignition_flag=6)
         else:
             ignition_type = IgnitionType(ignition_flag=ignition_flag)
 
@@ -1806,6 +1967,8 @@ class QU_TopoInputs(InputFile):
     Class representing the QU_TopoInputs.inp input file. This file
     contains advanced data pertaining to topography.
 
+    Attributes
+    ----------
     filename : str
         Path to the custom topo file (only used with option 5). Cannot be .bin. Use .dat or .inp
     topo_type : TopoType
diff --git a/quicfire_tools/outputs.py b/quicfire_tools/outputs.py
index 13c9ffe..027abb3 100644
--- a/quicfire_tools/outputs.py
+++ b/quicfire_tools/outputs.py
@@ -134,7 +134,7 @@
     },
 }
 EMISSIONS_OUTPUTS = {
-    "co-emissions": {
+    "co_emissions": {
         "file_format": "compressed",
         "dimensions": ["z", "y", "x"],
         "grid": "fire",
@@ -144,7 +144,7 @@
         "times in grams",
         "units": "g",
     },
-    "pm-emissions": {
+    "pm_emissions": {
         "file_format": "compressed",
         "dimensions": ["z", "y", "x"],
         "grid": "fire",
@@ -164,6 +164,35 @@
 
 
 class OutputFile:
+    """
+    A class representing a single output file. This class provides a common
+    interface for processing and retrieving data from output files.
+
+    Attributes
+    ----------
+    name: str
+        The name of the output file this object represents.
+    file_format: str
+        The format of the output file. Valid options are "gridded" and
+        "compressed".
+    dimensions: list[str]
+        The dimensions of the output file as ["z", "y", "x"] or ["y", "x"].
+    shape: tuple
+        The shape of the output file array data as (time, nz, ny, nx).
+    grid: str
+        The grid type of the output file. Valid options are "fire" and "quic".
+    delimiter: str
+        The delimiter used in the output file name.
+    extension: str
+        The file extension of the output file.
+    description: str
+        A description of the output file.
+    units: str
+        The units of the output file.
+    times: list[int]
+        A list of times corresponding to the timesteps.
+    """
+
     def __init__(
         self,
         name: str,
@@ -500,10 +529,12 @@ def to_numpy(self, key: str, timestep: None | int | list[int] = None) -> np.ndar
         >>> import quicfire_tools as qft
         >>> outputs = qft.SimulationOutputs("path/to/outputs", 50, 100, 100)
         >>> output_name = "fire-energy_to_atmos"
+        >>>
         >>> # Get all timesteps for the fire-energy_to_atmos output
         >>> fire_energy_all = outputs.to_numpy(output_name)
+        >>>
         >>> # Get the first timestep for the fire-energy_to_atmos output
-        >>> fire_energy_slice = outputs.to_numpy(output_name, timestep=0)
+        >>> fire_energy_first_time_step = outputs.to_numpy(output_name, timestep=0)
         """
         output = self._validate_output(key)
         return output.to_numpy(timestep)
@@ -529,8 +560,8 @@ def to_dask(self, key: str) -> da.Array:
         --------
         >>> import quicfire_tools as qft
         >>> outputs = qft.SimulationOutputs("path/to/outputs", 50, 100, 100)
-        >>> fire_energy = outputs.to_dask("fire-energy_to_atmos")
-        >>> fire_energy.compute()
+        >>> fire_energy_da = outputs.to_dask("fire-energy_to_atmos")
+        >>> fire_energy_np = fire_energy_da.compute()  # Retrieve the data
         """
         output = self._validate_output(key)
 
diff --git a/quicfire_tools/topography.py b/quicfire_tools/topography.py
index 5e9a05b..4b813cf 100644
--- a/quicfire_tools/topography.py
+++ b/quicfire_tools/topography.py
@@ -9,10 +9,14 @@
 # External Imports
 from typing import Literal
 
-from pydantic import BaseModel, Field, PositiveFloat, PositiveInt, SerializeAsAny
+from pydantic import BaseModel, Field, PositiveFloat, SerializeAsAny
 
 
 class TopoSources(int, Enum):
+    """
+    Enum class for all valid topography source options in QUIC-Fire.
+    """
+
     flat = 0
     gaussian_hill = 1
     hill_pass = 2
@@ -28,6 +32,11 @@ class TopoSources(int, Enum):
 
 
 class TopoType(BaseModel):
+    """
+    Base class for all topography types in QUIC-Fire. This class is used to
+    provide a common string representation for all topography types.
+    """
+
     topo_flag: SerializeAsAny[TopoSources]
 
     def __str__(self):
@@ -43,10 +52,25 @@ def __str__(self):
 
 
 class GaussianHillTopo(TopoType):
+    """
+    Creates a Gaussian hill topography in QUIC-Fire.
+
+    Attributes
+    ----------
+    x_hilltop : PositiveFloat
+        Gaussian hill top location x-direction [m]
+    y_hilltop : PositiveFloat
+        Gaussian hill top location y-direction [m]
+    elevation_max : PositiveFloat
+        Maximum elevation of the Gaussian hill [m]
+    elevation_std : PositiveFloat
+        Standard deviation of the Gaussian hill [m]
+    """
+
     topo_flag: SerializeAsAny[TopoSources] = TopoSources(1)
-    x_hilltop: PositiveInt
-    y_hilltop: PositiveInt
-    elevation_max: PositiveInt
+    x_hilltop: PositiveFloat
+    y_hilltop: PositiveFloat
+    elevation_max: PositiveFloat
     elevation_std: PositiveFloat
 
     def __str__(self):
@@ -61,8 +85,19 @@ def __str__(self):
 
 
 class HillPassTopo(TopoType):
+    """
+    Creates a hill pass topography in QUIC-Fire.
+
+    Attributes
+    ----------
+    max_height : PositiveFloat
+        Maximum elevation of the hill pass [m]
+    location_param : PositiveFloat
+        Location parameter of the hill pass [m]
+    """
+
     topo_flag: SerializeAsAny[TopoSources] = TopoSources(2)
-    max_height: PositiveInt
+    max_height: PositiveFloat
     location_param: PositiveFloat
 
     def __str__(self):
@@ -75,6 +110,17 @@ def __str__(self):
 
 
 class SlopeMesaTopo(TopoType):
+    """
+    Creates a slope mesa topography in QUIC-Fire.
+
+    Attributes
+    ----------
+    slope_axis : Literal[0, 1]
+        Slope axis (0 = x, 1 = y)
+    slope_value : float
+        Slope in dh/dx or dh/dy
+    """
+
     topo_flag: SerializeAsAny[TopoSources] = TopoSources(3)
     slope_axis: Literal[0, 1]
     slope_value: PositiveFloat
@@ -91,9 +137,26 @@ def __str__(self):
 
 
 class CanyonTopo(TopoType):
+    """
+    Creates a canyon topography in QUIC-Fire.
+
+    Attributes
+    ----------
+    x_location: PositiveFloat
+        Canyon location in x-dir [m].
+    y_location: PositiveFloat
+        Canyon location in y-dir [m].
+    slope_value: PositiveFloat
+        Slope in dh/dx or dy/dy [-].
+    canyon_std: PositiveFloat
+        Canyon function standard deviation [m].
+    vertical_offset: PositiveFloat
+        Canyon vertical offset [m].
+    """
+
     topo_flag: SerializeAsAny[TopoSources] = TopoSources(4)
-    x_start: PositiveInt
-    y_center: PositiveInt
+    x_location: PositiveFloat
+    y_location: PositiveFloat
     slope_value: PositiveFloat
     canyon_std: PositiveFloat
     vertical_offset: PositiveFloat
@@ -101,8 +164,8 @@ class CanyonTopo(TopoType):
     def __str__(self):
         flag_line = super().__str__()
         params = (
-            f"\n{self.x_start}\t! m, x-start of canyon on slope\n"
-            f"{self.y_center}\t! m, y-center of canyon"
+            f"\n{self.x_location}\t! m, x-start of canyon on slope\n"
+            f"{self.y_location}\t! m, y-center of canyon"
             f"{self.slope_value}\t! N/A, slope\n"
             f"{self.canyon_std}\t! m, std\n"
             f"{self.vertical_offset}\t! m, height offset of the bottom of the canyon"
@@ -111,9 +174,22 @@ def __str__(self):
 
 
 class HalfCircleTopo(TopoType):
+    """
+    Creates a half-circle topography in QUIC-Fire.
+
+    Attributes
+    ----------
+    x_location: PositiveFloat
+        The x-coordinate of the center of the half-circle topography [m].
+    y_location: PositiveFloat
+        The y-coordinate of the center of the half-circle topography [m].
+    radius: PositiveFloat
+        The radius of the half-circle topography [m].
+    """
+
     topo_flag: SerializeAsAny[TopoSources] = TopoSources(6)
-    x_location: PositiveInt
-    y_location: PositiveInt
+    x_location: PositiveFloat
+    y_location: PositiveFloat
     radius: PositiveFloat
 
     def __str__(self):
@@ -127,22 +203,44 @@ def __str__(self):
 
 
 class SinusoidTopo(TopoType):
+    """
+    Creates a sinusoidal topography in QUIC-Fire.
+
+    Attributes
+    ----------
+    period: PositiveFloat
+        The period of the sinusoidal wave [m].
+    amplitude: PositiveFloat
+        The amplitude of the sinusoidal wave [m].
+    """
+
     topo_flag: SerializeAsAny[TopoSources] = TopoSources(7)
     period: PositiveFloat
     amplitude: PositiveFloat
 
     def __str__(self):
         flag_line = super().__str__()
-        params = f"\n{self.period}\t! m, mean\n" f"{self.amplitude}\t! m, amplitude"
+        params = f"\n{self.period}\t! m, period\n" f"{self.amplitude}\t! m, amplitude"
         return flag_line + params
 
 
 class CosHillTopo(TopoType):
+    """
+    Creates a cosine-shaped hill topography in QUIC-Fire.
+
+    Attributes
+    ----------
+    aspect: PositiveFloat
+        The aspect (or orientation) of the hill [-].
+    height: PositiveFloat
+        The height of the hill [m].
+    """
+
     topo_flag: SerializeAsAny[TopoSources] = TopoSources(8)
     aspect: PositiveFloat
-    height: PositiveInt
+    height: PositiveFloat
 
     def __str__(self):
         flag_line = super().__str__()
-        params = f"\n{self.aspect}\t! m, mean\n" f"{self.height}\t! m, height"
+        params = f"\n{self.aspect}\t! [0], aspect\n" f"{self.height}\t! m, height"
         return flag_line + params


Test patch:
diff --git a/quicfire_tools/data/templates/latest/QUIC_fire.inp b/quicfire_tools/data/templates/latest/QUIC_fire.inp
index 914e11c..f7ff150 100644
--- a/quicfire_tools/data/templates/latest/QUIC_fire.inp
+++ b/quicfire_tools/data/templates/latest/QUIC_fire.inp
@@ -37,6 +37,6 @@ $mass_burnt_out		! Output vertically-integrated % mass burnt (fire grid)
 $firebrand_out		! Output trajectories firebrands
 $emissions_out		! Output compressed array emissions (fire grid)
 $radiation_out		! Output gridded thermal radiation (fire grid)
-$intensity_out      ! Output surface fire intensity at every fire time step
+$surf_eng_out      ! Output surface fire intensity at every fire time step
 ! AUTOKILL
 $auto_kill          ! Kill if the fire is out and there are no more ignitions or firebrands (0 = no, 1 = yes)
diff --git a/tests/test_input_module.py b/tests/test_input_module.py
index aeeae24..6629836 100644
--- a/tests/test_input_module.py
+++ b/tests/test_input_module.py
@@ -1048,7 +1048,7 @@ def test_to_dict(self):
         assert result_dict["firebrand_out"] == quic_fire.firebrand_out
         assert result_dict["emissions_out"] == quic_fire.emissions_out
         assert result_dict["radiation_out"] == quic_fire.radiation_out
-        assert result_dict["intensity_out"] == quic_fire.intensity_out
+        assert result_dict["surf_eng_out"] == quic_fire.surf_eng_out
 
     def test_from_dict(self):
         quic_fire = self.get_test_object()
@@ -1166,7 +1166,7 @@ def test_to_file(self):
         assert quic_fire.radiation_out == int(
             lines[current_line + 12].strip().split("!")[0]
         )
-        assert quic_fire.intensity_out == int(
+        assert quic_fire.surf_eng_out == int(
             lines[current_line + 13].strip().split("!")[0]
         )
         assert quic_fire.auto_kill == int(
@@ -1542,9 +1542,9 @@ def test_complex_inputs(self):
             "8 = cos hill, 9 = QP_elevation.inp, "
             "10 = terrainOutput.txt (ARA), "
             "11 = terrain.dat (firetec)\n"
-            "100\t! m, x-center\n"
-            "150\t! m, y-center\n"
-            "500\t! m, max height\n"
+            "100.0\t! m, x-center\n"
+            "150.0\t! m, y-center\n"
+            "500.0\t! m, max height\n"
             "20.0\t! m, std"
         )
         assert topoinputs.smoothing_method == 1
@@ -1646,9 +1646,11 @@ def test_to_file(self):
             11: 0,
         }
         add = add_dict.get(topo_flag)
-        assert int(lines[3].strip().split("!")[0]) == topoinputs.topo_type.x_hilltop
-        assert int(lines[4].strip().split("!")[0]) == topoinputs.topo_type.y_hilltop
-        assert int(lines[5].strip().split("!")[0]) == topoinputs.topo_type.elevation_max
+        assert float(lines[3].strip().split("!")[0]) == topoinputs.topo_type.x_hilltop
+        assert float(lines[4].strip().split("!")[0]) == topoinputs.topo_type.y_hilltop
+        assert (
+            float(lines[5].strip().split("!")[0]) == topoinputs.topo_type.elevation_max
+        )
         assert (
             float(lines[6].strip().split("!")[0]) == topoinputs.topo_type.elevation_std
         )
@@ -1907,7 +1909,7 @@ def test_set_output_files(self):
         assert sim_inputs.quic_fire.fuel_moist_out == 0
         assert sim_inputs.quic_fire.mass_burnt_out == 1
         assert sim_inputs.quic_fire.radiation_out == 0
-        assert sim_inputs.quic_fire.intensity_out == 0
+        assert sim_inputs.quic_fire.surf_eng_out == 0
         assert sim_inputs.quic_fire.emissions_out == 2
 
     def test_set_custom_simulation(self):
@@ -1918,7 +1920,7 @@ def test_set_custom_simulation(self):
         assert sim_inputs.quic_fire.fuel_moisture is None
         assert sim_inputs.quic_fire.fuel_height is None
         assert sim_inputs.quic_fire.ignition_type == IgnitionType(
-            ignition_flag=IgnitionSources(6)
+            ignition_flag=IgnitionSources(7)
         )
         assert sim_inputs.qu_topoinputs.topo_type == TopoType(topo_flag=TopoSources(5))