diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 2aac3a29..ca0291e6 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -46,3 +46,19 @@ jobs:
with:
name: spider-sciath-test-dir
path: test_dir
+
+ installer-smoke:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v6
+ - name: Install Python requirements
+ run: pip install -r py/requirements.txt
+ - name: Run installer
+ run: ./tools/get_spider.sh
+ - name: Test with SciATH
+ run: |
+ cd tests && git clone https://github.com/sciath/sciath --depth=1 && cd -
+ export PYTHONPATH=$PYTHONPATH:$PWD/tests/sciath
+ mkdir -p test_dir && cd test_dir
+ python -m sciath -d -w pth.conf
+ python -m sciath -f -w pth.conf ../tests/tests.yml
\ No newline at end of file
diff --git a/.github/workflows/docs.yaml b/.github/workflows/docs.yaml
index 644d1707..e2fb3571 100644
--- a/.github/workflows/docs.yaml
+++ b/.github/workflows/docs.yaml
@@ -5,25 +5,25 @@ on:
- master
- main
permissions:
- contents: write
+ contents: read
+ pages: write
+ id-token: write
jobs:
deploy:
+ environment:
+ name: github-pages
+ url: ${{ steps.deployment.outputs.page_url }}
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@v4
- - name: Configure Git Credentials
- run: |
- git config user.name github-actions[bot]
- git config user.email 41898282+github-actions[bot]@users.noreply.github.com
+ - uses: actions/configure-pages@v5
+ - uses: actions/checkout@v5
- uses: actions/setup-python@v5
with:
python-version: 3.x
- - run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
- - uses: actions/cache@v4
+ - run: pip install zensical markdown-include pymdown-extensions mkdocstrings mkdocstrings-python mkdocs-material mkdocs-bibtex
+ - run: zensical build --clean
+ - uses: actions/upload-pages-artifact@v4
with:
- key: mkdocs-material-${{ env.cache_id }}
- path: ~/.cache
- restore-keys: |
- mkdocs-material-
- - run: pip install mkdocs-material markdown-include pymdown-extensions mkdocstrings mkdocstrings-python mkdocs-bibtex
- - run: mkdocs gh-deploy --force
+ path: site
+ - uses: actions/deploy-pages@v4
+ id: deployment
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index cfc273df..e13574af 100644
--- a/.gitignore
+++ b/.gitignore
@@ -50,3 +50,9 @@ verification/
*.lot
*.synctex.gz
*.toc
+
+# petsc directory
+petsc*/
+
+# site
+site/
\ No newline at end of file
diff --git a/README.md b/README.md
index 819ce3b2..dc1f2bc6 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,18 @@
# SPIDER
**Simulating Planetary Interior Dynamics with Extreme Rheology**
-
-
-[](https://github.com/djbower/spider/actions)
-[](https://doi.org/10.5281/zenodo.5682523)
-
-
+
+
+
+
+
+
+
+
+
+
+
+
A 1-D parameterised interior dynamics code for rocky planets with molten and/or solid interiors and support for volatile cycling, redox reactions, and radiative transfer in the atmosphere.
diff --git a/docs/Explanations/abe1993.md b/docs/Explanations/abe1993.md
new file mode 100644
index 00000000..b6c75490
--- /dev/null
+++ b/docs/Explanations/abe1993.md
@@ -0,0 +1,98 @@
+---
+tags:
+ - phase separation
+ - magma ocean
+ - thermal evolution
+---
+
+# SPIDER: model overview
+
+Here you can find a detailed overview of the SPIDER formulation.
+
+!!! note
+ This model overview is taken from the [notes](https://github.com/FormingWorlds/SPIDER/tree/main/notes/) and contains an extended description of the equations and derivations related to the SPIDER code. It is still **work in progress.**
+
+Notes specific to derivations in [^cite-ABE93].
+
+## Phase Separation
+
+Under the assumption of no melting/solidification, melt-solid separation is treated as a mass-transfer process. Average density of mixture:
+
+$$\frac{1}{\rho} = \frac{1}{\rho_s}(1-\phi)+\frac{1}{\rho_m}\phi$$
+
+where $\phi$ is mass fraction of melt.
+
+The masses of solid and melt phases per unit volume are:
+
+$$\rho_s^\ast \equiv (1-\phi) \rho = \frac{\rho_s \rho_m (1-\phi)}{\rho_s \phi + \rho_m(1-\phi)}$$
+
+$$\rho_m^\ast \equiv \phi \rho = \frac{\rho_s \rho_m \phi}{\rho_s \phi + \rho_m(1-\phi)}$$
+
+Define the velocity of the local barycenter:
+
+$$v \equiv \phi v_m + (1-\phi) v_s$$
+
+And the vertical mass flux of melt relative to the barycenter:
+
+$$J_m \equiv \rho \phi (1-\phi)(v_m-v_s)$$
+
+The phase separation equation becomes:
+
+$$\frac{\partial \phi}{\partial t} + v \frac{\partial \phi}{\partial z} = \frac{\rho_m \rho_s}{\rho (\rho_s-\rho_m)} \frac{\partial v}{\partial z} = - \frac{1}{\rho} \frac{\partial J_m}{\partial z}$$
+
+## Time Scale
+
+Characteristic time scale $\tau$ of melt-solid separation for a partially molten layer of thickness $L$:
+
+$$\tau = \frac{\rho L}{2 J_m} \min (\phi_0, 1-\phi_0 )$$
+
+## Impact Stirring
+
+During planetary accretion, planetesimal impacts stirred the mantle. Assuming a roughly linear accretion rate:
+
+$$\frac{dm}{dt} \sim \frac{M_E}{\tau_{acc}}$$
+
+The impactor mass distribution is:
+
+$$\frac{dN}{dm} = {K_{max}\left(\frac{m}{M_{max}}\right)^{-q}}$$
+
+with $q=1.5$ and $M_{max}=0.1 M_E$.
+
+The depth-dependent impact stirring timescale is:
+
+$$\tau_{s}(d) = \frac{\tau_{acc}}{N_{s}(d)}$$
+
+where $N_{s}$ is the effective number of complete stirring events at depth $d$.
+
+## Thermal Evolution of a Magma Ocean
+
+The energy (enthalpy) balance equation:
+
+$$\frac{\partial H}{\partial t} = - \frac{1}{\rho}\nabla \cdot \vec{J_{tot}} + \Delta V_m|g|\vec{J_m} \cdot \hat{r} + q_{heat}$$
+
+The total heat flux is a combination of sensible and latent heat:
+
+$$J_{tot} = J_q + T\Delta S_m J_m$$
+
+The melt fraction can be approximated as:
+
+$$\phi(H) = \frac{H - H_{sol}}{T\Delta S_m(P)}$$
+
+for $H_{sol} < H < H_{liq}$.
+
+Key assumptions for single-component modeling:
+- Use a single component melting curve roughly corresponding to the 50% solidus
+- Entropy of melting adjusted to ensure bounds match realistic mantle
+- Use realistic heat capacity and density values for both phases
+- Chemical differentiation negligible (second order effect)
+- Thermal range of partially molten region (~200 K) small compared to mantle temperature difference (~2500 K)
+
+## Gravitational potential energy
+
+The gravitational potential energy per unit mass is:
+
+$$E_{\rm grav} = - \frac{G M(r)}{r} = - |g(r)|r$$
+
+Melting influences gravitational potential energy through its effect on $g(r)$. However, since $g(r)$ is an integrated quantity it is not sensitive to small changes in density distribution. We therefore neglect changes in $E_{\mathrm{grav}}$ for silicate melting in a magma ocean.
+
+[^cite-ABE93]: Yutaka Abe, *Thermal Evolution and Chemical Differentiation of the Terrestrial Magma Ocean*, Evolution of the Earth and Planets, 1993.
diff --git a/docs/Explanations/atmosphere.md b/docs/Explanations/atmosphere.md
new file mode 100644
index 00000000..3930cbaa
--- /dev/null
+++ b/docs/Explanations/atmosphere.md
@@ -0,0 +1,150 @@
+---
+tags:
+ - volatiles
+ - outgassing
+ - atmospheric escape
+---
+
+# SPIDER: model overview
+
+Here you can find a detailed overview of the SPIDER formulation.
+
+!!! note
+ This model overview is taken from the [notes](https://github.com/FormingWorlds/SPIDER/tree/main/notes/) and contains an extended description of the equations and derivations related to the SPIDER code. It is still **work in progress.**
+
+## Volatile Mass Balance
+
+The mass balance of a given volatile in the interior [^cite-LMC13] is:
+
+$$X_v^s M^s + X_v^l M^l + X_v^g M^g + m_v^e + m_v^o + m_v^r = X_v^{\rm init} M^m$$
+
+where superscripts $s$, $l$, $g$, $e$, $o$, $t$ denote solid, liquid (melt), gas, escaped, ocean, and total. **Only solid, liquid, and atmosphere are physical reservoirs.**
+
+The partition coefficient relates volatile concentrations:
+
+$$k_v = \frac{X_v^s}{X_v}$$
+
+### Atmospheric mass
+
+The total atmospheric mass of $q$ species composes as:
+
+$$m_t^g = \frac{4 \pi R_p^2}{g} P_s$$
+
+where $R_p$ is planetary radius and $P_s$ is surface pressure.
+
+The mass of a given volatile species is:
+
+$$m_v^g = 4 \pi R_p^2 \left( \frac{\mu_v^g}{\bar{\mu}} \right) \frac{p_v}{g}$$
+
+Partial pressure follows a modified (power-law) Henry's law:
+
+$$p_v ( X_v ) = \left( \frac{X_v}{\alpha_v} \right)^{\beta_v}$$
+
+In SPIDER we use scaled mass (omitting the $4 \pi$ factor):
+
+$$X_v (k_v M^s + M^l) + \frac{R_p^2}{g} \left( \frac{\mu_v^g}{\bar{\mu}} \right) p_v + m_v^e + m_v^o + m_v^r = X_v^{\rm init} M^m$$
+
+We solve for volatile mass fraction in the liquid phase, from which we can compute volatile mass in solid and gas phases.
+
+## Non-dimensionalisation
+
+### Mass
+
+Masses are non-dimensionalised as:
+
+$$M = \rho_0 R_0^3 \hat{M}$$
+
+### Volatile Concentration
+
+Volatile concentration is expressed as scaled mass fraction:
+
+$$X_v = V_0 \hat{X}_v$$
+
+where $V_0=10^{-6}$ gives parts-per-million (ppm), $V_0=10^{-2}$ gives weight percent (wt%), and $V_0=1$ gives mass fraction.
+
+### Power Law Solubility
+
+Non-dimensional partial pressure:
+
+$$\hat{p}_v ( \hat{X}_v ) = \left( \frac{\hat{X}_v}{\hat{\alpha}_v} \right)^{\beta_v}$$
+
+where:
+
+$$\hat{\alpha}_v = \frac{\alpha_v^{{\rm ppm/Pa}^{1/\beta_v}}}{10^6} \frac{P_0^\frac{1}{\beta_v}}{V_0}$$
+
+## Sossi Solubility
+
+For H$_2$O [^cite-SF17]:
+
+$$X_v = A{f_{H_2O}}^\frac{1}{2}+B G f_{H_2O}$$
+
+where fugacities are constrained by oxygen buffer, and $A=534$ ppm/bar$^{0.5}$ and $B=723$ ppm/bar.
+
+## Initial Volatile Concentration
+
+For an initial condition, we prescribe the total volatile concentration and solve the mass balance to obtain initial partial pressure consistent with chemical equilibrium criteria.
+
+## Chemical Reactions
+
+Reactions transfer mass between volatile species. For example:
+
+$$[\rm{H}_2O]\leftrightarrow \frac{1}{2} [\rm{O}_2] + [\rm{H}_2]$$
+
+with equilibrium constant:
+
+$$K=\frac{p_{\rm H_2} f_{\rm O_2}^{1/2}}{p_{\rm H_2\rm O}}$$
+
+Mass is conserved through stoichiometry:
+
+$$m_{H_2O} = m_{O2} + m_{H_2}$$
+
+## Atmospheric Escape
+
+### Jeans escape
+
+$$\frac{d m_v^e}{dt} = \left( \frac{d m_{\rm v}^{\rm g}}{dt} \right) \mathcal{R} (1 + \lambda_s) \exp(-\lambda_s) + \frac{\Phi}{4 \pi}$$
+
+where Jeans parameter:
+
+$$\lambda_s = \frac{g R_p \mu_{\rm v}}{k_b T_s N_A}$$
+
+### Zahnle escape model
+
+For H$_2$ [^cite-ZGC19]:
+
+$$\phi_{H_2} \approx \Gamma \frac{(1 \times 10^{12}) f_{H_2} S}{\sqrt{1+0.006S^2}}$$
+
+where $S$ is non-dimensional and $\Gamma$ is a scaling constant.
+
+## Grey atmosphere model
+
+### Optical depth
+
+$$\tau^\ast = \frac{3 \kappa^\prime p(\tau^\ast)}{2g}$$
+
+### Effective emissivity
+
+Optical depths for each volatile are combined:
+
+$$\epsilon = \frac{2}{\sum_j \tau_j^\ast +2}$$
+
+### Atmosphere temperature structure
+
+Temperature as function of optical depth [^cite-AM85]:
+
+$$T(\tau^\ast) = \left( T_0^4 \frac{(\tau^\ast+1)}{2} +T_\infty^4 \right)^\frac{1}{4}$$
+
+where:
+
+$$T_0 = \left( \frac{F_{atm}}{\sigma} \right)^\frac{1}{4}$$
+
+### Stellar flux
+
+$$F_{sun} = \sigma T_{eqm}^4 = (1-\alpha) \frac{F_0^\prime}{D^2}$$
+
+where $\alpha$ is bolometric albedo, $F_0'$ is averaged solar constant, and $D$ is planet-star distance.
+
+[^cite-LMC13]: Lebrun, T.; Massol, H.; Chassefi\`ere, E.; Davaille, A.; Marcq, E.; Sarda, P.; Leblanc, F.; Brandeis, G., *Thermal evolution of an early magma ocean in interaction with the atmosphere*, J. Geophys. Res.-Planet., 2013.
+[^cite-SF17]: Laura Schaefer; Bruce Fegley, *Redox States of Initial Atmospheres Outgassed on Rocky Planets and Planetesimals*, Astrophys. J., 2017.
+[^cite-ZGC19]: Kevin J. Zahnle; Marko Gacesa; David C. Catling, *Strange messenger: A new history of hydrogen on Earth, as told by Xenon*, Geochim. Cosmochim. Acta, 2019.
+[^cite-AM85]: Abe, Yutaka; Matsui, Takafumi, *The formation of an impact-generated H2O atmosphere and its implications for the early thermal history of the Earth*, J. Geophys. Res. Solid Earth, 1985.
diff --git a/docs/Explanations/model.md b/docs/Explanations/basic_thermodynamics.md
similarity index 75%
rename from docs/Explanations/model.md
rename to docs/Explanations/basic_thermodynamics.md
index b8380573..eaf36fbf 100644
--- a/docs/Explanations/model.md
+++ b/docs/Explanations/basic_thermodynamics.md
@@ -14,36 +14,36 @@ Here you can find a detailed overview of the SPIDER formulation.
## Thermodynamic energy transport and enthalpy fluxes {#sect:thermodynamic}
-**Goal:** Understand the origin of the energy transport associated with the enthalpy of chemical/phase components. This derives the energy equation for a multi-component system that appears in the appendix of [@ABE95].
+**Goal:** Understand the origin of the energy transport associated with the enthalpy of chemical/phase components. This derives the energy equation for a multi-component system that appears in the appendix of [^cite-ABE95].
-In the following section, I combine the notation from [@DM62] and [@ABE95] so beware of notation changes compared to the original papers. Where a choice can be made, I typically stick to the notation of [@ABE95].
+In the following section, I combine the notation from [^cite-DM62] and [^cite-ABE95] so beware of notation changes compared to the original papers. Where a choice can be made, I typically stick to the notation of [^cite-ABE95].
## Conservation and entropy balance
-Conservation of mass fraction ([@DM62 Eq. II.13], also [@ABE95 Eq. A2]), *where $i$ is a thermodynamic (chemical) component and $j$ refers to a reaction*:
+Conservation of mass fraction ([^cite-DM62], Eq. II.13, also [^cite-ABE95], Eq. A2), *where $i$ is a thermodynamic (chemical) component and $j$ refers to a reaction*:
$$
\rho \frac{D \omega_i}{Dt} = -\nabla \cdot \vec{J}_i + \rho \sum_{j=1}^r \nu_{ij} \mathcal{J}_j \qquad (i=1,\ n)
\tag{1}\label{eq:ABE95_A2}
$$
-Here I define $\mathcal{J}_j$ as the rate of reaction $j$ per unit mass (same as $w_j$ in [@ABE95] but this notation may be confused with mass fraction $\omega$ which is why I change the symbol). [@ABE95] defines $\nu_{ij}$ as the mass of component $i$ formed by reaction $j$. Note that [@DM62] use slightly different definitions of these quantities, since they define $\mathcal{J}$ as a mass per unit volume and unit time and omit the leading $\rho$ term.
+Here I define $\mathcal{J}_j$ as the rate of reaction $j$ per unit mass (same as $w_j$ in [^cite-ABE95] but this notation may be confused with mass fraction $\omega$ which is why I change the symbol). [^cite-ABE95] defines $\nu_{ij}$ as the mass of component $i$ formed by reaction $j$. Note that [^cite-DM62] use slightly different definitions of these quantities, since they define $\mathcal{J}$ as a mass per unit volume and unit time and omit the leading $\rho$ term.
-Entropy balance ([@DM62 Eq. III.12], also [@ABE95 Eq. A4]):
+Entropy balance ([^cite-DM62], Eq. III.12, also [^cite-ABE95], Eq. A4):
$$
\rho \frac{Ds}{Dt} = - \nabla \cdot \vec{J}_s + \sigma
\tag{2}\label{eq:DM62_ch3_eq12}
$$
-where the entropy flux $\vec{J}_s$ is the difference between the total entropy flux $\vec{J}_{s,\ tot}$ and a convective term $\rho s v$ ([@DM62 Eq. III.13]):
+where the entropy flux $\vec{J}_s$ is the difference between the total entropy flux $\vec{J}_{s,\ tot}$ and a convective term $\rho s v$ ([^cite-DM62], Eq. III.13):
$$
\vec{J}_s = \vec{J}_{s,\ tot} - \rho s v
\tag{3}
$$
-The entropy balance, *excluding viscous dissipation and external forces*, is ([@DM62 Eq. III.19]):
+The entropy balance, *excluding viscous dissipation and external forces*, is ([^cite-DM62], Eq. III.19):
$$
\rho \frac{Ds}{Dt} = - \nabla \cdot \left( \frac{\vec{J}_q - \sum_i \mu_i \vec{J}_i}{T} \right)
@@ -53,14 +53,14 @@ $$
\tag{4}
$$
-Where entropy flux is ([@DM62 Eq. III.20]):
+Where entropy flux is ([^cite-DM62], Eq. III.20):
$$
\vec{J}_s = \frac{1}{T} \left( \vec{J}_q - \sum_{i=1}^n \mu_i \vec{J}_i \right)
\tag{5}
$$
-and entropy production is ([@DM62 Eq. III.21]):
+and entropy production is ([^cite-DM62], Eq. III.21):
$$
\sigma =
@@ -70,23 +70,23 @@ $$
\tag{6}
$$
-By using the thermodynamic relation ([@DM62 Eq. III.23]):
+By using the thermodynamic relation ([^cite-DM62], Eq. III.23):
$$
T d \left( \frac{\mu_i}{T} \right) = \left( d \mu_i \right)_T - \frac{h_i}{T} dT
\tag{7}
$$
-we can define a new flux as ([@DM62 Eq. III.24]):
+we can define a new flux as ([^cite-DM62], Eq. III.24):
$$
\vec{J}_q^\prime = \vec{J}_q - \sum_{i=1}^n h_i \vec{J}_i
\tag{8}\label{eq:Jqprime}
$$
-*Eq. $\ref{eq:Jqprime}$ is the definition of heat flux used by [@ABE95] and therefore it removes the energetic contribution associated with the transport of enthalpy by the components. This term then reappears as an entropy source term, since of course the physics must remain the same!*
+*Eq. $\ref{eq:Jqprime}$ is the definition of heat flux used by [^cite-ABE95] and therefore it removes the energetic contribution associated with the transport of enthalpy by the components. This term then reappears as an entropy source term, since of course the physics must remain the same!*
-Then entropy flow is ([@DM62 Eq. III.26], also [@ABE95 Eq. A5]):
+Then entropy flow is ([^cite-DM62], Eq. III.26, also [^cite-ABE95], Eq. A5):
$$
\vec{J}_s = \frac{1}{T} \vec{J}_q^\prime + \sum_{i=1}^n s_i \vec{J}_i
@@ -95,7 +95,7 @@ $$
where $s_i = -(\mu_i-h_i)/T$ is the partial specific entropy of component $i$. Written in this way the entropy flux contains the heat flow $\vec{J}_q^\prime$ and a transport of partial entropies with respect to the barycentric velocity $v$.
-The entropy production associated with this definition can be written as ([@DM62 Eq. III.25], also [@ABE95 Eq. A6]):
+The entropy production associated with this definition can be written as ([^cite-DM62], Eq. III.25, also [^cite-ABE95], Eq. A6):
$$
\sigma =
@@ -105,11 +105,11 @@ $$
\tag{10}\label{eq:DM62_ch3_eq25}
$$
-### Quoted from [@DM62]
+**Quoted from [^cite-DM62]**
> “It is clear that the difference between $\vec{J}_q$ and $\vec{J}_q^\prime$ (Eq. $\ref{eq:Jqprime}$) represents a transfer of heat due to diffusion. Therefore the quantity $\vec{J}_q^\prime$ also represents an irreversible heat flow. In fact in diffusing mixtures the concept of heat flow can be defined in different ways. Obviously a different definition of the notion of heat flux leaves all physical results unchanged. But to any particular choice corresponds a special form of the entropy production $\sigma$. It is a matter of expediency which choice is the most suitable in a particular application of the theory. The freedom of defining the heat flow in various ways, of which the possibility was indicated here in the framework of a macroscopic treatment, exists also in the microscopic theories of transport phenomena in mixtures.”
-Abe chooses to model $J_q^\prime$ as a convective heat flux using mixing length theory. In this regard, [@ABE95 Eq. 47] excludes the energetic contribution of the enthalpy transport of the components (but remember it appears later in Abe's formulation). Now, using the above equations we can derive [@ABE95 Eq. A10] using several vector identities:
+Abe chooses to model $J_q^\prime$ as a convective heat flux using mixing length theory. In this regard, [^cite-ABE95], Eq. 47 excludes the energetic contribution of the enthalpy transport of the components (but remember it appears later in Abe's formulation). Now, using the above equations we can derive [^cite-ABE95], Eq. A10 using several vector identities:
$$
\begin{aligned}
@@ -148,7 +148,7 @@ $$
\tag{13}
$$
-Collect terms. *[@ABE95] missing $\rho$ on the RHS*:
+Collect terms. *[^cite-ABE95] missing $\rho$ on the RHS*:
$$
\rho \frac{Ds}{Dt}
@@ -169,7 +169,7 @@ $$
\tag{15}
$$
-Leading to [@ABE95 Eq. A10], *[@ABE95 Eq. A10] missing $\rho$*:
+Leading to [^cite-ABE95], Eq. A10, *[^cite-ABE95], Eq. A10 missing $\rho$*:
$$
\boxed{
@@ -180,7 +180,7 @@ $$
\tag{16}
$$
-How [@ABE95 Eq. 4] is derived from this point is confusing. It appears that if you literally swap out components and replace them with phases, where the two phases are melt and solid, you can reproduce [@ABE95 Eq. 4]. But then later, [@ABE95] states that this equation must be “modified for multi-component systems, because energy transport due to mass transport must be taken into account”. He then goes onto to derive the following equation, which makes sense based on [@ABE95 Eq. A10]. The transport of thermodynamic components can be divided between a melt and solid phase. *This is how the notion of phases is introduced into the formulation.*
+How [^cite-ABE95], Eq. 4 is derived from this point is confusing. It appears that if you literally swap out components and replace them with phases, where the two phases are melt and solid, you can reproduce [^cite-ABE95], Eq. 4. But then later, [^cite-ABE95] states that this equation must be “modified for multi-component systems, because energy transport due to mass transport must be taken into account”. He then goes onto to derive the following equation, which makes sense based on [^cite-ABE95], Eq. A10. The transport of thermodynamic components can be divided between a melt and solid phase. *This is how the notion of phases is introduced into the formulation.*
## Introducing phases (melt / solid)
@@ -252,7 +252,7 @@ This is helpful because the gravitational separation term (first term) can be pa
## Two-phase entropy equation
-For two phases ($n=2$) and $J_m=-J_s$, *[@ABE95 Eq. 4] missing $\rho$*:
+For two phases ($n=2$) and $J_m=-J_s$, *[^cite-ABE95], Eq. 4 missing $\rho$*:
$$
\boxed{
@@ -265,7 +265,7 @@ $$
Note this contains a latent heat ($\Delta h$) associated with melt–solid separation ($\vec{J}_m$). At chemical equilibrium, which we always assume, $\mu_m=\mu_s$ and hence the last term is zero.
-Now, for multi-component systems, energy transport due to mass transport must be taken into account (*[@ABE95 Eq. 20] missing $\rho$*):
+Now, for multi-component systems, energy transport due to mass transport must be taken into account (*[^cite-ABE95], Eq. 20 missing $\rho$*):
$$
\begin{aligned}
@@ -291,4 +291,5 @@ The two major steps that we now need to perform are:
1. Recast the velocities in terms of relative velocities, often relative to the centre of mass (barycentric velocity).
2. Parameterise the resulting fluxes that originate from considering relative velocities.
-\bibliography
\ No newline at end of file
+[^cite-ABE95]: Yutaka Abe, *Basic equations for evolution of partially molten mantle and core*, The Earth's Central Part: Its Structure and Dynamics, 1995.
+[^cite-DM62]: S. R. De Groot; P. Mazur, *Non-Equilibrium Thermodynamics*, North-Holland Publishing Company, Amsterdam, 1962.
diff --git a/docs/Explanations/boundary_conditions.md b/docs/Explanations/boundary_conditions.md
new file mode 100644
index 00000000..65a8dbf6
--- /dev/null
+++ b/docs/Explanations/boundary_conditions.md
@@ -0,0 +1,91 @@
+---
+tags:
+ - boundary conditions
+ - surface flux
+ - core-mantle boundary
+---
+
+# SPIDER: model overview
+
+Here you can find a detailed overview of the SPIDER formulation.
+
+!!! note
+ This model overview is taken from the [notes](https://github.com/FormingWorlds/SPIDER/tree/main/notes/) and contains an extended description of the equations and derivations related to the SPIDER code. It is still **work in progress.**
+
+## Surface radiative-interior flux balance
+
+### Algebraic approach (preferred, implemented)
+
+From energy balance, we balance the interior and geothermal heat flux at the surface:
+
+$$J_{rad} = \epsilon \sigma (T_0^4 - T_{eqm}^4) = J_{tot} = J_{conv} + J_{cond}$$
+
+where $T_0$ is surface temperature determined from surface entropy $S_0$.
+
+The surface energy balance becomes:
+
+$$\epsilon \sigma (T_0^4 - T_{eqm}^4) = -\rho_1 \kappa_{h1} T_0 \left. \left( \frac{\partial S}{\partial r} \right|_0 \right)$$
+
+At each time step, we construct the entropy profile using the current solution and solve iteratively to determine the surface entropy gradient and surface entropy satisfying this boundary condition. This approach allows $J_{rad}$ to be imposed through any arbitrary relation since we do not require derivatives.
+
+### Ultra-thin thermal boundary layer parameterisation
+
+The thermal boundary layer at a magma ocean surface is expected to be very thin with large temperature drops. High resolution mesh is impractical for 2-D models. Therefore, we parameterise the temperature drop across the ultra-thin boundary layer.
+
+From mixing length theory, heat flux is:
+
+$$J_q = \rho c_p \kappa_{\rm h} \left[ \frac{\partial T}{\partial z}-\left( \frac{\partial T}{\partial z} \right)_s \right] + \rho c_p \kappa \frac{\partial T}{\partial z}$$
+
+For viscous case [^cite-ABE93]:
+
+$$\kappa_h = \frac{\alpha g l^4}{18 \nu} \left[ \frac{\partial T}{\partial z}-\left( \frac{\partial T}{\partial z} \right)_s \right]$$
+
+We can rearrange to solve for $\partial T / \partial z$ with a root-finding algorithm:
+
+$$\frac{J_q}{\rho c_p} - \kappa \frac{\partial T}{\partial z} - \kappa_h \left[ \frac{\partial T}{\partial z} - \left( \frac{\partial T}{\partial z} \right)_s \right] = 0$$
+
+Integrating from a surface temperature downward gives the temperature profile. Plotting $\Delta T$ (temperature drop) versus $F_{\rm top}$ (surface heat flux) in log-log space yields a straight line:
+
+$$\log(\Delta T) = A \log( F_{\rm top} ) + B$$
+
+with $A \approx 3/4$. This gives:
+
+$$\Delta T = c T_s^3$$
+
+with constant $c=8.05154344{\rm E-08}$, providing a temperature drop estimate as a function of surface temperature.
+
+### Lid formation
+
+In 1-D, a high viscosity lid can form at the surface with thickness comparable to smallest mesh spacing. Since surface temperature falls through mixed phase region before interior regions, a thin impermeable lid develops that insulates the magma ocean. This behavior is unphysical and computationally expensive.
+
+**Solution**: Use a constant mixing length (rather than distance-to-boundary definition) to allow more transport near surface, mitigating thin viscous lid formation.
+
+### Foundering upper boundary layer
+
+As magma ocean cools through the mixed phase, the rheological transition creates an insulating blanket at the surface. This introduces unphysical oscillations in total heat content as the stiff upper boundary layer alternately locks up, releases, and locks up again.
+
+Standard mixing length theory successfully captures continuous foundering of cool material at the upper boundary. However, it fails for a solidified crust where phase contrast and rheological difference with underlying mantle create coherent structures that persist and remelt at depth.
+
+## Core-mantle boundary
+
+### Universal boundary condition
+
+The core energy balance is:
+
+$$\frac{dQ_c}{dt} = m_c c_c \frac{dT_{core}}{dt} = -A_{cmb} J_{cmb} + E_{in}$$
+
+where $E_{in}$ is heat flux from core (including internal heating):
+
+$$E_{in} = H_c m_c = J_{in} A_{cmb}$$
+
+The time update for entropy gradient at CMB:
+
+$$\frac{\Delta r}{2} \frac{d}{dt} \left( \left. \frac{dS}{dr} \right |_{cmb} \right) = (-E_{cmb}+E_{in}) \left( \frac{c_{cmb}}{c_c} \right) \left( \frac{1}{m_c \hat{T}_{core} T_{cmb}} \right) - \frac{dS_{-1}}{dt}$$
+
+This enables three boundary conditions via choice of $E_{in}$:
+
+1. **$E_{in}=0$**: CMB cools entirely by flux removed by magma ocean
+2. **$E_{in}=\text{constant}$**: Constant heat flux from core buffers CMB cooling
+3. **$E_{in}=E_{cmb}$**: Isothermal boundary condition (flux from core balances ocean cooling)
+
+[^cite-ABE93]: Yutaka Abe, *Thermal Evolution and Chemical Differentiation of the Terrestrial Magma Ocean*, Evolution of the Earth and Planets, 1993.
diff --git a/docs/Explanations/hybrid_eos.md b/docs/Explanations/hybrid_eos.md
new file mode 100644
index 00000000..79e234b4
--- /dev/null
+++ b/docs/Explanations/hybrid_eos.md
@@ -0,0 +1,86 @@
+---
+tags:
+ - equation of state
+ - EOS
+ - thermodynamic consistency
+---
+
+# SPIDER: model overview
+
+Here you can find a detailed overview of the SPIDER formulation.
+
+!!! note
+ This model overview is taken from the [notes](https://github.com/FormingWorlds/SPIDER/tree/main/notes/) and contains an extended description of the equations and derivations related to the SPIDER code. It is still **work in progress.**
+
+## Hybrid equation of state from multiple data sources
+
+We construct a pseudo self-consistent thermodynamic model using data from different sources. Typically data is expressed as functions of pressure $P$ only: $\rho(P)$, $\alpha(P)$, $c_p(P)$ for solid and melt phases, with constant $k$ and $\eta$. We also require liquidus and solidus curves and reference entropy of fusion $\Delta S_{\rm fus}$.
+
+## General outline of approach
+
+### Reference liquid adiabat
+
+At reference pressure $P_0$, determine the liquidus temperature. Compute a reference liquid adiabat by integrating:
+
+$$\left( \frac{dT}{dP} \right)_S = \frac{\alpha T}{c_p \rho}$$
+
+where $S=S_0$ by definition. This is our reference liquid adiabat.
+
+### Liquidus to entropy space
+
+Map the liquidus from temperature to entropy space by measuring departure from the reference adiabat:
+
+$$dS = \left( \frac{c_P}{T} \right) dT$$
+
+$$\Delta S = \int_{T_1}^{T_{2}} \frac{c_P}{T} dT$$
+
+where $T_1$ is temperature along reference adiabat and $T_2$ is liquidus temperature.
+
+### Solidus curve
+
+Repeat above steps for the solidus to obtain a solidus curve as entropy perturbation from a solid reference adiabat.
+
+### Entropy of fusion
+
+The entropy difference between liquidus and solidus is constrained by entropy of melting. For MgSiO3, entropy of melting is constant to within ~3% across Earth's mantle pressure range [^cite-SKS09].
+
+Approaches:
+1. Modeler prescribes $\Delta S_{\rm fus}$ directly at $P_0$
+2. Modeler provides enthalpy $\Delta h$ at $P_0$: $\Delta S_{\rm fus} = \Delta h / T_0$
+3. Integrate heat capacity in mixed phase region (complex, requires feedback from $\Delta S_{\rm fus}$)
+
+### Mixed phase properties
+
+Pin entropy difference between liquidus and solidus at reference pressure, assuming relatively constant with pressure. Compute all necessary quantities in mixed phase region:
+
+$$T(P) = \frac{T_s + T_l}{2}$$
+
+$$\rho(P) = \frac{1}{(1-\phi)/\rho_s + \phi/\rho_m}$$
+
+$$C_P = C_{s,P}(1-\phi) + C_{m,P}\phi + \Delta S_{\rm fus} T$$
+
+$$\alpha = (1-\phi)\alpha_s + \phi \alpha_m$$
+
+$$\left( \frac{dT}{dP} \right)_S = \frac{\alpha T}{C_p \rho}$$
+
+### Thermodynamic consistency
+
+**Important issue**: Liquidus and solidus computed independently may not maintain constant entropy of fusion across pressure ranges. Liquidus can drop below solidus, which is nonsensical.
+
+To enforce thermodynamic consistency, we must "tweak something":
+
+1. **Adjust material properties** to get reasonable entropy of fusion while keeping liquidus/solidus fixed in temperature space
+2. **Adjust melting curves** in entropy space while keeping material properties fixed, accepting mismatch to original temperature curves
+3. **Combine both approaches** with systematic inversion
+
+**Recommendation**: Request user specify only one melting curve bound (liquidus OR solidus) plus entropy of melting and temperature change at reference pressure. Calculate the other curve fully self-consistently.
+
+### Temperature from entropy
+
+For final use, our code requires temperature as function of entropy:
+
+$$T(P) = T_{\rm ad}(P) \exp{ \left( \frac{\Delta S(P)}{c_P(P)} \right) }$$
+
+where $T_{\rm ad}$ is reference adiabat temperature and $\Delta S$ is entropy perturbation from adiabat.
+
+[^cite-SKS09]: Lars Stixrude; Nico de Koker; Ni Sun; Mainak Mookherjee; Bijaya B. Karki, *Thermodynamics of silicate liquids in the deep Earth*, Earth Planet. Sci. Lett., 2009.
diff --git a/docs/Explanations/internal_heating.md b/docs/Explanations/internal_heating.md
new file mode 100644
index 00000000..87fbd796
--- /dev/null
+++ b/docs/Explanations/internal_heating.md
@@ -0,0 +1,53 @@
+---
+tags:
+ - radiogenic heating
+ - radionuclides
+ - internal heat
+---
+
+# SPIDER: model overview
+
+Here you can find a detailed overview of the SPIDER formulation.
+
+!!! note
+ This model overview is taken from the [notes](https://github.com/FormingWorlds/SPIDER/tree/main/notes/) and contains an extended description of the equations and derivations related to the SPIDER code. It is still **work in progress.**
+
+## Radioactive heating
+
+The concentration $X_i$ of a given isotope $i$ is [^cite-TS14]:
+
+$$X_i(t_\mathrm{age})= X_{i0} \exp{\left( \frac{t_\mathrm{age} \ln2}{T_{i1/2}} \right)}$$
+
+where $X_{i0}$ is present-day concentration, $t_\mathrm{age}$ is age (time before present), and $T_{i1/2}$ is half-life.
+
+With $t_{i0}$ as the time at which the concentration is known (e.g., 4.54 Gyrs for present-day):
+
+$$X_i(t) = X_{i0} \exp{\left( \frac{(t_{i0}-t) \ln2}{T_{i1/2}} \right)}$$
+
+The heat production rate for isotope $i$ is:
+
+$$H_i(t)=H_i x_{i0} C_0 \exp{\left( \frac{(t_{i0}-t) \ln2}{T_{i1/2}} \right)}$$
+
+where $x_{i0}$ is the fractional isotopic abundance and $C_0$ is the elemental concentration.
+
+### Radionuclides
+
+Key radionuclides relevant for planetary heating:
+
+| Isotope | T$_{1/2}$ (Myr) | $x_{i0}$ | $H_i$ (W/kg) |
+|---------|-----------------|---------|-------------|
+| $^{26}$Al | 0.717 | 0 | 0.3583 |
+| $^{40}$K | 1248 | $1.1668\times10^{-4}$ | $2.8761\times10^{-5}$ |
+| $^{60}$Fe | 2.62 | 0 | $3.6579\times10^{-2}$ |
+| $^{232}$Th | 14000 | 1 | $2.6368\times10^{-5}$ |
+| $^{235}$U | 704 | 0.0072045 | $5.68402\times10^{-4}$ |
+| $^{238}$U | 4468 | 0.9927955 | $9.4946\times10^{-5}$ |
+
+Total heating rate is the sum:
+
+$$H(t) = \sum_i H_i(t)$$
+
+Note that [^cite-RUE17] computes bulk element power ensuring internal consistency.
+
+[^cite-TS14]: Turcotte, D.; Schubert, G., *Geodynamics*, Cambridge University Press, 2014.
+[^cite-RUE17]: Thomas Ruedas, *Radioactive heat production of six geologically important nuclides*, Geochem. Geophy. Geosys., 2017.
diff --git a/docs/Explanations/mass_coordinates.md b/docs/Explanations/mass_coordinates.md
new file mode 100644
index 00000000..d3a23815
--- /dev/null
+++ b/docs/Explanations/mass_coordinates.md
@@ -0,0 +1,109 @@
+---
+tags:
+ - mass coordinates
+ - coordinate transformation
+ - hydrostatic pressure
+---
+
+# SPIDER: model overview
+
+Here you can find a detailed overview of the SPIDER formulation.
+
+!!! note
+ This model overview is taken from the [notes](https://github.com/FormingWorlds/SPIDER/tree/main/notes/) and contains an extended description of the equations and derivations related to the SPIDER code. It is still **work in progress.**
+
+## Mass coordinate definition
+
+ [^cite-ABE95] defines:
+
+$$\frac{4 \pi}{3} \rho_0 \xi^3 \equiv 4 \pi \int_0^r \rho r^2 dr = m$$
+
+We similarly define, since we only consider the mantle bounded between $r_{\rm cmb}$ and $r$:
+
+$$\frac{4 \pi}{3} \rho_0 (\xi^3-\xi_{\rm cmb}^3) \equiv 4 \pi \int_{\rm cmb}^r \rho r^2 dr = m$$
+
+Taking the derivative with respect to $r$:
+
+$$\rho_0 \xi^2 \frac{d \xi}{d r} = \rho r ^2$$
+
+Integrating:
+
+$$r(\xi) = \left[ r_{\rm cmb}^3 + 3 \int_{\xi_{\rm cmb}}^\xi \frac{\rho_0}{\rho(\xi)} \xi^2 d\xi \right]^\frac{1}{3}$$
+
+Note that $\xi$ is referred to as a "mass coordinate" and has **length dimension**. This equation is the inverse transformation, allowing conversion between radius $r$ and mass coordinate $\xi$.
+
+### Implementation
+
+Currently, using the Adams-Williamson EOS we compute $\rho_0$ analytically using the same integration limits for $\xi$ and $r$, i.e., $\xi_{\rm surf}=r_{\rm surf}$ and $\xi_{\rm cmb}=r_{\rm cmb}$. Hence $\rho_0$ is the 'actual' average density of the mantle. We then prescribe a mesh using $\xi$; each $\xi$ corresponds to a shell containing mass $m$.
+
+For the non-linear solution, it is natural to set the $\xi$ coordinate as the initial guess for the $r$ coordinate. The Jacobian of the objective function is simply the infinitesimal mass segment in physical coordinates $r^2 \rho(r)$.
+
+## Partial derivatives
+
+We compute the partial derivatives under the coordinate transformation from $r \rightarrow \xi$, with time $t$ remaining as the second variable:
+
+$$\left( \frac{\partial}{\partial r} \right)_t = \left( \frac{\partial \xi}{\partial r} \right)_t \left( \frac{\partial}{\partial \xi} \right)_t = \frac{\rho}{\rho_0} \left( \frac{r}{\xi} \right)^2 \left(\frac{\partial}{\partial \xi} \right)_t$$
+
+$$\left( \frac{\partial}{\partial t} \right)_r = \left( \frac{\partial}{\partial t} \right)_\xi - U \left( \frac{\partial}{\partial r} \right)_t$$
+
+where $U$ is the local barycentric velocity in the radial direction.
+
+### Implementation
+
+We time step the following equation in SPIDER:
+
+$$\rho T \left( \frac{\partial s}{\partial t} \right)_\xi = - \frac{\rho}{\rho_0 \xi^2} \left(\frac{\partial}{\partial \xi} \right)_t \left( r^2 \left[ \vec{J}_q + \Delta h (\vec{J}_m + \vec{J}_{cm}) \right] \right)$$
+
+This description is known as the "Lagrangian description" [^cite-KWW12] because we are following mass elements.
+
+#### Integral form
+
+[^cite-BSW18] instead solve the integral form:
+
+$$\int_V \rho T \left( \frac{\partial s}{\partial t} \right)_\xi dV = - \int_A F \cdot n dA + \int_V \rho H dV$$
+
+#### Fluxes
+
+Heat flux becomes:
+
+$$\vec{J}_q=-\rho T \kappa_h \frac{\partial S}{\partial r} = -\rho T \kappa_h \frac{\rho}{\rho_0} \left( \frac{r}{\xi} \right)^2 \left(\frac{\partial S}{\partial \xi} \right)_t$$
+
+## Hydrostatic pressure
+
+We use the Adams-Williamson equation of state to compute the hydrostatic pressure profile.
+
+### Pressure
+
+Adams-Williamson equation of state:
+
+$$P = - \frac{\rho_r g}{\beta} (\exp( \beta z )-1)$$
+
+where $\rho_r$ is reference surface density, $g$ gravity, $\beta$ compressibility, and $z$ is depth.
+
+Pressure gradient:
+
+$$\frac{dP}{dr} = \rho_s g \exp( \beta z )$$
+
+Density is:
+
+$$\rho(r) = \rho_s \exp( \beta (r_0 - r) )$$
+
+For any such EOS with a simple relation between $\rho$ and $r$, we can integrate to directly compute the mass coordinate $\xi$ for a given $r$:
+
+$$\xi = \left[ \xi_{\rm cmb}^3 + \frac{3}{\rho_0} \int_{\rm cmb}^r \rho r^2 dr \right] ^ {1/3}$$
+
+An intuitive option is to set $\rho_0$ such that $\xi=1.0$ at the planetary surface, with $\xi=0$ at the innermost boundary.
+
+### General formulation
+
+$$\frac{dp(r)}{dr} = g(r) \rho(r) = -G \frac{M(r)}{r^2}\rho(r)$$
+
+Multiply by $r^2/\rho$ and differentiate with respect to $r$:
+
+$$\frac{1}{r^2} \frac{d}{dr} \left( \frac{r^2}{\rho}\frac{dp}{dr} \right) = -4 \pi G \rho$$
+
+Combined with an EOS of the form $p=p(\rho)$, this is an ordinary second order differential equation for the density or pressure.
+
+[^cite-ABE95]: Yutaka Abe, *Basic equations for evolution of partially molten mantle and core*, The Earth's Central Part: Its Structure and Dynamics, 1995.
+[^cite-KWW12]: Rudolf Kippenhahn; Alfred Weigert; Achim Weiss, *Stellar Structure and Evolution*, Springer, 2012.
+[^cite-BSW18]: Dan J. Bower; Patrick Sanan; Aaron S. Wolf, *Numerical solution of a non-linear conservation law applicable to the interior dynamics of partially molten planets*, Phys. Earth Planet. Inter., 2018.
diff --git a/docs/Explanations/material_properties.md b/docs/Explanations/material_properties.md
new file mode 100644
index 00000000..f44d00a5
--- /dev/null
+++ b/docs/Explanations/material_properties.md
@@ -0,0 +1,296 @@
+---
+tags:
+ - material properties
+ - equation of state
+ - viscosity
+---
+
+# SPIDER: model overview
+
+!!! note
+ This model overview is taken from the [notes](https://github.com/FormingWorlds/SPIDER/tree/main/notes/) and contains an extended description of the equations and derivations related to the SPIDER code. It is still **work in progress.**
+
+## Material Properties
+
+### Mantle EOS
+
+A simple approximation is made to calculate the thermodynamic properties of the mantle silicates, representing them as a pseudo-single-component system.
+
+We consider a mantle that is everywhere in local thermodynamic equilibrium, resulting in uniform compositional profiles. Driven by buoyancy differences, crystals that form are displaced from their point of crystallization, instantaneously removing heat as they are resorbed by the magma ocean (according to Clausius Clapeyron). Crystal-melt separation should cause chemical differentiation, however if the timescales for turbulent mixing are sufficiently fast, the mantle will remain homogenous. Furthermore, persistent differentiation (which is expected for the later stages of crystallization as viscosity rises and timescales lengthen) represents a second-order correction on top of the first order energetics represented by convection, heat dissipation, and crystallization of a uniform composition magma ocean.
+
+We represent the thermodynamics of mantle melting using a pseudo-one-component model, which retains the simplicity of a standard one-component model, while introducing the important multi-component characteristic of a finite temperature interval for melting. We develop this model in response to the arguments of [^cite-SA07], which shows how many of the most important aspects of mantle melting can be reasonably-represented by a simple one-component model when viewed in pressure-entropy space. Besides its lack of chemical evolution, the primary shortcoming of this approach is the absence of a thermal melting interval, which arises due to differential chemical partitioning between solid and melt phases, as demonstrated by simple systems with binary loops.
+
+ It is a useful first approximation to consider a crystallizing magma ocean as a chemically homogenous system, where the evolving chemistries of its constituent phases are assumed to remain in thermodynamic equilibrium maintaining constant bulk composition. We can therefore approximate the energetics of this system by modifying a standard one-component system, replacing the univariant melting curve in P-T space $T_{\rm fus}(P)$ by a melting interval.
+
+The single-component melting curve is given by $T_{\rm fus}(P)$ and $S_{\rm fus}(P)$, and is defined by the condition of equilibrium, where the solid and melt phases have equal Gibbs energies at the melting temperature for each pressure.
+
+The modified melting interval is centered on the fusion curve, bounded by the liquidus above and solidus below:
+
+$$T_{\rm liq}(P) = T_{\rm fus}(P) + \Delta T_{\rm fus}(P) / 2$$
+
+$$T_{\rm sol}(P) = T_{\rm fus}(P) - \Delta T_{\rm fus}(P) / 2$$
+
+where the melting interval width is defined by $\Delta T_{\rm fus}(P)=\delta T \cdot T_{\rm fus}(P)$ with $\delta T \approx 0.07$ throughout the mantle [^cite-SKS09].
+
+### Solid EOS
+
+For the Mosenfelder MgSiO3 model, we use the EOS for MgSiO3 melt and Mg-endmember bridgmanite as given by the global fit to shock wave and diamond anvil cell data in [^cite-M09]. Since this paper does not fully define all of the needed EOS parameters, we rely upon the estimated melting region (bounded by solidus and liquidus profiles) for the mantle given by [^cite-SKS09]. It is important to recognize that [^cite-M09] does not present a thermodynamically consistent model of this system, due to its use of the Mie-Grüneisen formulation, which assumes that the grüneisen parameter is independent of temperature together with its model for the heat capacity of the melt, which induces temperature dependence to the adiabatic compression curves.
+
+The primary thermodynamic property controlling magma ocean evolution is the grüneisen parameter, $\gamma$, which controls the adiabatic thermal profile through a convecting system:
+
+$$\frac{dT}{dP}\bigg|_S = \gamma \left( \frac{T}{K_S} \right)$$
+
+The *thermodynamic* definition of the grüneisen parameter is given by:
+
+$$\gamma \equiv -\frac{\partial \log_e{T}}{\partial \log_e{V}}\bigg|_S = \frac{\alpha K_S V}{C_P}$$
+
+where $\alpha$ is the thermal expansion, $K_S$ is the adiabatic bulk modulus, and $C_P$ is the constant pressure heat capacity. In the case of solids, the grüneisen parameter is approximately independent of temperature at low to moderate temperatures, as a consequence of nearly harmonic atomic vibrations for small vibrational amplitudes (as described by the quasi-harmonic approximation, QHA). This allows us to integrate the equation above to obtain an expression for the temperature along the reference adiabat, $T_{0S}(V)$:
+
+$$T_{0S}(V) = T_0 \exp \left[ -\int_{V_0}^{V} \frac{\gamma(V)}{V} dV \right]$$
+
+where $T_0$ is the reference adiabatic temperature at 0 GPa.
+
+The standard power-law expression gives a simple and reasonable representation over wide compression ranges:
+
+$$\gamma(V) = \gamma_0 \left(\frac{V}{V_0}\right)^q$$
+
+where $\gamma_0$ and $q$ give the value at the reference state and the compression dependence.
+In this form, the reference adiabatic temperature simplifies to:
+
+$$T_{0S}(V) = T_0 \exp \left[ -(\gamma - \gamma_0)/q \right]$$
+
+Though this temperature-independent form of the grüneisen parameter is often used in shock wave experiments, the underlying assumptions of QHA are strongly violated at significant fractions of the melting temperature.
+The most dramatic consequence of the strong anharmonicity present at high temperatures is that thermal expansions are overestimated by 30-100% near melting [^cite-WW09], challenging the ability to accurately model melting phase relations.
+
+To maintain a physically reasonable model, we apply lowest-order perturbation theory to account for anharmonicity (e.g. [^cite-ZK71], [^cite-OD03]).
+Under near-melting conditions, the grüneisen parameter is no longer independent of temperature, and thus we first describe the reference adiabat for the solid, $T_{0S}^{\rm sol}$ by defining the grüneisen parameter evolution along a reference adiabatic compression curve, $\gamma_{0S}^{\rm sol}$.
+Since magma ocean modeling is concerned only with temperatures near or above the solidus, we are free to choose a zero-pressure reference temperature of $T_0^{\rm sol}$=1000 K to ensure that we are in the classical Dulong-Petit limit, yielding a constant heat capacity.
+The lowest-order anharmonic correction yields a temperature-dependent heat capacity:
+
+$$C_V^{\rm sol} = 3N k_B (1 - aT)$$
+
+where $k_B$ is Boltzmann's constant, $N$ is the number of atoms per formula unit, $a$ is the volume-dependent anharmonic correction factor.
+We assume a power-law dependence for the anharmonicity factor:
+
+$$a(V) = a_0 \left( \frac{V}{V_0} \right)^m$$
+
+where the reference value $a_0$ and its compression dependence $m$ must be determined empirically.
+The entropy gain relative to the reference adiabat is obtained by integration:
+
+$$\Delta S_0^{\rm sol}(V,T) = \int_{T_{0S}^{\rm sol}}^T \frac{C_V^{\rm sol}}{T} dT = 3N k_B \left[ \log_e (T/T_{0S}^{\rm sol}) - a(T - T_{0S}^{\rm sol}) \right]$$
+
+where $T_{0S}^{\rm sol}(V)$ is given by the adiabat equation above.
+By taking partial derivatives with respect to $V$ and $T$, we obtain the total differential:
+
+$$\frac{dS^{\rm sol}}{3N k_B} = \left[ \frac{1}{T} - a \right] dT + \left[ - \frac{1}{T_{0S}^{\rm sol}}\frac{d T_{0S}^{\rm sol}}{dV} -\frac{da}{dV}(T-T_{0S}^{\rm sol}) + a \frac{dT_{0S}^{\rm sol}}{dV}\right]dV$$
+
+By setting $dS^{\rm sol}=0$ and rearranging, we obtain the expression for the grüneisen parameter allowing in the presence of anharmonic effects:
+
+$$\gamma^{\rm sol}(V,T) = \frac{(1-a T_{0S}^{\rm sol})\gamma_{0S}^{\rm sol} - (T-T_{0S}^{\rm sol})ma}{1-aT}$$
+
+where if we assume zero anharmonicity ($a=0$) or restrict ourselves to the reference adiabat ($T=T_{0S}^{\rm sol}$), we recover the reference grüneisen evolution given by $\gamma_{0S}^{\rm sol}(V)$.
+
+The internal energy expression is similarly determined by integration along a pathway up the reference adiabat and then up to the target temperature $(V_0,T_0) \rightarrow (V,T_{0S}) \rightarrow (V,T)$:
+
+$$\Delta E_0^{\rm sol}(V,T) = -\int_{V_0}^V P_S^{\rm sol}(V) dV + \int_{T_{0S}}^{T} C_V^{\rm sol} dT$$
+
+$$= -\int_{V_0}^V P_S^{\rm sol}(V) dV + 3N k_B [ (T-T_{0S}^{\rm sol}) - a/2(T^2 - {T_{0S}^{\rm sol}}^2) ]$$
+
+where $P_S^{\rm sol}(V)$ describes the compression curve of the reference adiabat, described using the Vinet EOS:
+
+$$P_S(V) = 3K_{0S}(1-x)x^{-2}\exp\left[\nu(1-x)\right]$$
+
+$$\text{where } x=(V/V_0)^{1/3} \text{ and } \nu=\frac{3}{2}(K'_{0S}-1)$$
+
+where $V_0$, $K_{0S}$, and $K'_{0S}$, are the zero pressure volume, adiabatic bulk modulus, and its pressure derivative.
+The total pressure is given by the volume derivative:
+
+$$P^{\rm sol}(V,T) = P_S^{\rm sol} + 3N k_B\left[(1-aT)\frac{dT}{dV}\bigg|_S - (1-aT_{0S}^{\rm sol})\frac{dT_{0S}^{\rm sol}}{dV} \right]$$
+
+$$+ \frac{3}{2}N k_B \frac{da}{dV}(T^2 - {T_{0S}^{\rm sol}}^2)$$
+
+### Liquid EOS
+
+To represent the thermodynamics of mantle material in a simple and flexible way, we derive a new parameterization high pressure melt EOS which captures melt behavior while retaining physically meaningful parameters that are easily interpreted in the context of magma ocean crystallization [^cite-WB18].
+
+### Solution
+
+#### Density: Volume–mass proportionality
+
+For each component $i$:
+
+$$\rho_i = \frac{m_i}{v_i}$$
+
+For solution:
+
+$$\rho_{sol}=\frac{m_{sol}}{v_{sol}} = \frac{\sum_i m_i}{v_{sol}} = \sum_i \frac{m_i}{v_{sol}} = \sum_i \frac{m_i}{v_i} \frac{v_i}{v_{sol}} = \sum_i \rho_i \left( \frac{v_i}{v_{sol}} \right)$$
+
+**Assume volume is proportional to mass**, for both the solution and the pure phase:
+
+$$v \propto m$$
+
+Now **assume proportionality constant $c$ is the same for both the solution and the pure phase**. This means the two substances have similar pure densities:
+
+$$v = c m$$
+
+Therefore, continuing from above by substituting in the assumptions:
+
+$$\rho_{sol}=\sum_i \rho_i \left( \frac{v_i}{v_{sol}} \right) = \sum_i \rho_i \left( \frac{c m_i}{c \sum_i m_i} \right) = \sum_i \rho_i \omega_i$$
+
+where $\omega_i$ is mass fraction of component $i$:
+
+$$\omega_i = \frac{m_i}{\sum_i m_i} = \frac{m_i}{M}$$
+
+This is perhaps the most used and "intuitive" description of the density of a solution. It conveniently means that the density of a solution is the addition of the mass-weighted (pure) densities of the components:
+
+$$\rho_{sol}=\sum_i \rho_i \omega_i$$
+
+#### Density: Volume additivity
+
+$$\omega_i = \frac{m_i}{M}$$
+
+$$\frac{\omega_i}{\rho_i} = \frac{m_i}{M} \frac{v_i}{m_i} = \frac{v_i}{M}$$
+
+From above:
+
+$$\therefore \sum_i \left( \frac{\omega_i}{\rho_i} \right) = \frac{1}{M} \sum_i v_i$$
+
+Now **assume volumes are additive**. Typically true for ideal solutions and immiscible, non-reacting mixtures:
+
+$$\frac{1}{M} \sum_i v_i = \frac{V}{M} = \frac{1}{\rho} \implies \frac{1}{\rho} = \sum_i \left( \frac{\omega_i}{\rho_i} \right)$$
+
+where, as before, $\omega$ is the mass fraction of component $i$ in the mixture. This is what we currently use in SPIDER to calculate the density of the mixture based on the end-member densities of MgSiO₃ melt and solid.
+
+#### Solution properties
+
+We modify the single-component silicate (bridgmanite) model to accommodate a partially-molten (mixed-phase) region to reasonably approximate the behaviour of a multi-component mantle. This relies on 2 approximations:
+
+1. Chemical differentiation is negligible (second order effect, at least energetically)
+2. The thermal range of the partially molten region (~200 K) is small compared to the temperature difference across the mantle (~2500 K). Values from [^cite-SKS09].
+
+This is achieved by fitting a single-component fusion curve to the 50% solidus line for a realistic mantle chemistry. Then we define a melting interval centred about the fusion curve to ensure solidus and liquidus enthalpy/entropy bounds that match the "true" mantle system:
+
+$$S_{\rm liq}(P) = S_{\rm fus}(P) + \Delta S_{\rm fus}(P) / 2$$
+$$S_{\rm sol}(P) = S_{\rm fus}(P) - \Delta S_{\rm fus}(P) / 2$$
+
+where $S$ is entropy, $P$ pressure, and $S_{\rm fus}$ is the fusion curve. Subscripts "liq" and "sol" denote that the quantity is determined at the liquidus and solidus, respectively. The entropy of fusion, $\Delta S_{\rm fus}$ is computed to ensure the liquidus and solidus approximate those for a multi-component mantle.
+
+!!! note "Side note"
+ The melting curve used by [^cite-ABE93] is from Ohtani (1983), although it may be slightly adjusted. Ohtani appears to assume a constant entropy change on melting (fairly reasonable assumption) of 8.03 cal/mol/K for MgSiO3 melt. The enthalpy change in [^cite-ABE93] is likely calculated from this value, $\Delta h = T \Delta S$.
+
+To obtain the properties of the two phase aggregate, we represent the entropy of the system as a linear mixture of the solid and liquid values, thus providing a simple estimate of the melt fraction as a function of total entropy:
+
+$$\phi=
+\begin{cases}
+ 1 & \text{for } S>S_{\rm liq} \\
+ (S-S_{\rm sol}) / \Delta S_{\rm fus} & \text{for } S_{\rm sol}S_{\rm liq} \\
+ \phi T_{\rm liq} + (1-\phi) T_{\rm sol} & \text{for } S_{\rm sol} 0$, and hence $dS/dr$ is driven to a tiny value.
+
+[^cite-ABE95]: Yutaka Abe, *Basic equations for evolution of partially molten mantle and core*, The Earth's Central Part: Its Structure and Dynamics, 1995.
diff --git a/docs/How-to/external_mesh_input.md b/docs/How-to/external_mesh_input.md
new file mode 100644
index 00000000..fc1804f3
--- /dev/null
+++ b/docs/How-to/external_mesh_input.md
@@ -0,0 +1,48 @@
+# External mesh input
+
+SPIDER can accept a pre-computed mesh from an external file instead of computing one internally from the Adams-Williamson equation of state. This enables coupling with structure solvers like [Zalmoxis](https://proteus-framework.org/Zalmoxis) that provide more accurate density profiles for non-Earth-like compositions.
+
+## 1. Enable external mesh mode
+
+Set these options when running SPIDER:
+
+| Option | Type | Description |
+|--------|------|-------------|
+| `-MESH_SOURCE` | `int` | `0` (default): internal AW mesh. `1`: read from external file. |
+| `-mesh_external_filename` | `string` | Path to external mesh file (required when `MESH_SOURCE=1`). |
+
+## 2. Prepare the mesh file
+
+The mesh file is plain text (SI units), ordered from surface to CMB.
+
+```text
+#
+r_b[0] P_b[0] rho_b[0] g_b[0] (surface)
+r_b[1] P_b[1] rho_b[1] g_b[1]
+...
+r_b[nb-1] P_b[nb-1] rho_b[nb-1] g_b[nb-1] (CMB)
+r_s[0] P_s[0] rho_s[0] g_s[0] (staggered)
+...
+r_s[ns-1] P_s[ns-1] rho_s[ns-1] g_s[ns-1]
+```
+
+!!! note "Rules"
+ - Columns are: radius [m], pressure [Pa], density [kg/m$^3$], gravity [m/s$^2$]
+ - Ordering is surface (largest radius) to CMB (smallest radius)
+ - Gravity should be negative (inward)
+ - `ns = nb - 1`
+ - First line starts with `#` and contains `nb` and `ns`
+
+## 3. Quick example
+
+```bash
+# From SPIDER root
+python tests/generate_aw_mesh.py -n 50 -o mesh.dat
+./spider -options_file tests/opts/blackbody50.opts -MESH_SOURCE 1 -mesh_external_filename mesh.dat
+```
+
+!!! info "EOS out-of-range"
+ With EOS lookup tables such as WolfBower2018, SPIDER issues a **one-time warning per table and direction** when pressure or entropy lies outside the tabulated range; subsequent out-of-range queries are silently clamped.
+
+ If the thermal expansion coefficient alpha becomes negative near table boundaries, it is reset to zero with a warning to prevent NaNs in the mixing length theory.
+
diff --git a/docs/How-to/installation.md b/docs/How-to/installation.md
index c60cacca..17b2da9c 100644
--- a/docs/How-to/installation.md
+++ b/docs/How-to/installation.md
@@ -1,233 +1,120 @@
# SPIDER: installation
!!! note
- The standard way of installing this version of SPIDER is within the PROTEUS Framework, as described in the [PROTEUS installation guide](https://proteus-framework.org/PROTEUS/installation.html#11-setup-spider-interior-evolution-model).
+ The standard way of installing this version of SPIDER is within the PROTEUS Framework, as described in the [PROTEUS installation guide](https://proteus-framework.org/PROTEUS/installation.html#11-setup-spider-interior-evolution-model).
-## 1. Quick Installation
+## Quick installation
-Here we provide a short installation guide to get you up and running with SPIDER. First, we install PETSc which provides the solver library and then we install SPIDER. Finally, we can (optionally) install a [test harness](https://github.com/sciath/sciath).
+Here we provide a short installation guide to get you up and running with SPIDER.
-1. Test you have a valid C compiler installed by running the following command in a terminal window (install a C compiler if this command fails):
+!!! info "Quadruple precision"
+ To install SPIDER manually with quadruple precision, follow [this guide](quadruple_installation.md).
- ```
- echo '#include' > t.c && echo 'int main(){printf("It seems to work!\n");}' >> t.c && gcc t.c && ./a.out && rm -f t.c a.out
- ```
+The recommended workflow is:
-2. If you are already a user of PETSc, comment out any existing references to `PETSC_DIR` and `PETSC_ARCH` in your`.profile`, `.bash_profile`,`.bashrc`, etc. and clear these variables from your current terminal session.
+1. Set up a Conda environment
+2. Clone SPIDER
+3. Run the installer from inside the SPIDER checkout
+4. The installer will automatically install PETSc first if it is missing
+5. Then it will build SPIDER against that PETSc installation
+!!! info "Are you in PROTEUS?"
+ When SPIDER is located at `PROTEUS/SPIDER`, PETSc is installed automatically into `PROTEUS/petsc`. When SPIDER is cloned standalone, PETSc is installed automatically into `SPIDER/petsc`.
-3. Download PETSc. Use this specific version as given below if interested in reproducing previous results, though any version of PETSc greater than or equal to 3.17 is expected to work just as well.
+### 0. Prerequisites
- ```
- cd /somewhere/to/install
- git clone https://gitlab.com/petsc/petsc -b main petsc-double
- cd petsc-double
- git checkout 63b725033a15f75ded7183cf5f88ec748e60783b
- ```
-4. Configure PETSc:
+You need:
- ```
- ./configure --with-debugging=0 --with-fc=0 --with-cxx=0 --with-cc=gcc --download-sundials2 --download-mpich --COPTFLAGS="-g -O3" --CXXOPTFLAGS="-g -O3"
- ```
+- a working C compiler;
+- `make`;
+- `git`;
+- MPI compiler wrappers available via `mpicc`, or permission for PETSc to download MPICH automatically.
+- About 20 minutes of your time.
-5. Follow the terminal instructions to complete the installation of PETSc. You can copy-paste the commands returned to you in the terminal window. Make note of `PETSC_DIR` and `PETSC_ARCH`, which are also reported in the terminal window.
+A basic test to check you have a working compiler is:
-6. In your environment, set `PETSC_DIR` and `PETSC_ARCH` to the PETSc installation. `PETSC_ARCH` will look something like arch-xxx-yyy (e.g. arch-darwin-c-opt). This completes the setup of PETSc:
+```bash
+echo '#include' > t.c && echo 'int main(){printf("It seems to work!\n");}' >> t.c && gcc t.c && ./a.out && rm -f t.c a.out
+```
- ```
- export PETSC_DIR=/somewhere/to/install/petsc-double
- export PETSC_ARCH=arch-xxx-yyy
- ```
+To ensure you have everything installed, run:
-7. Now download SPIDER:
+=== "Ubuntu / Debian"
- ```
- cd /somewhere/to/install
- git clone https://github.com/FormingWorlds/SPIDER.git
- cd SPIDER
+ ```bash
+ sudo apt install build-essential git libopenmpi-dev
```
-8. Make SPIDER:
+=== "Fedora / RHEL"
+ ```bash
+ sudo dnf install gcc git openmpi openmpi-devel lapack lapack-devel lapack-static f2c f2c-libs
```
- make clean
- make -j
- ```
-
- SPIDER is now installed and you can in principle skip to *Running a Model* below. However, you are advised to install the test harness as follows:
-9. [Optional] Get SciATH (Scientific Application Test Harness), which is a Python module:
+=== "macOS (Homebrew)"
- ```
- cd /somewhere/to/install
- git clone https://github.com/sciath/sciath -b dev
+ ```zsh
+ brew install gcc open-mpi
+ xcode-select --install
```
-10. [Requires SciATH] Add the resulting module to your Python path (for example):
+!!! note "HPC clusters"
- ```
- export PYTHONPATH=$PYTHONPATH:$PWD/sciath
- ```
+ On HPC clusters you usually **do not have sudo access**. In that case:
-11. [Requires SciATH] Now return to the root SPIDER directory and test basic functionality:
+ 1. Load any compiler / MPI modules provided by your cluster, for example:
+ ```bash
+ module avail
+ module load gcc
+ module load openmpi
```
- make test
- ```
-
-12. [Requires SciATH] You can also run all available tests by navigating to the `tests/` directory and running:
- ```
- python -m sciath tests.yml
- ```
+### 1. Create a Conda environment [optional]
-You should now be ready to use the code. Proceed to *Running a Model* to learn how to run a basic model and use SPIDER options files.
+!!! warning "Python version"
+ PETSc requires Python <= 3.12. If you do not make use of Conda, make sure your active environment uses a compatible version.
-## 2. Detailed Installation
+If you use Conda, it is recommended to build and run SPIDER inside a dedicated environment. This keeps the Python dependencies for SPIDER and optional tools such as SciATH separate from your base environment.
-The following section provides more general information about the installation process, and in particular provides the steps for installing SPIDER with support for quadruple precision calculations.
+Create and activate a Python 3.12 environment:
-Installation of SPIDER and its dependencies requires:
+```bash
+conda create -n spider python=3.12
+conda activate spider
+```
-1. A working C compiler
-2. Build dependencies (double or quadruple precision)
-3. Build SPIDER
+### 2. Clone SPIDER
-#### 2.1 C Compiler
-
-Any C compiler can be used if you want to build SPIDER for double precision calculations. However, for quadruple precision calculations you will need a GCC compiler. Unfortunately on a Mac, "`gcc`" is a wrapper for the default Apple compiler ("`clang`") which is not actually a GCC compiler package and hence does not support quadruple precision math. Particularly for Mac OSX you may choose to install GCC to support both double and quadruple precision calculations using a single compiler.
-
-A basic test to ensure you have a working compiler is (note you can swap out "`gcc`" for another compiler binary name):
-
-```
-echo '#include' > t.c && echo 'int main(){printf("It seems to work!\n");}' >> t.c && gcc t.c && ./a.out && rm -f t.c a.out
+```bash
+cd /somewhere/to/install
+git clone https://github.com/FormingWorlds/SPIDER.git
+cd SPIDER
```
-For Mac OSX, the easiest way to install GCC is from
+### 3. Run the installer
-If you use MacPorts, Homebrew, or apt, these can also be used to install GCC. *Unfortunately, the GCC compiler from Macports seems to be frequently broken*. Nevertheless, for example, using MacPorts:
-
-```
-sudo port install gcc8
+```bash
+./tools/get_spider.sh
```
-This will install a set of GCC binaries, typically in "`/opt/local/bin`". The C compiler (for GCC8) will be called "`gcc-mp-8"` where the mp is obviously clarifying that it was installed by MacPorts.
-Now on a Mac, you will usually access the default Apple compiler ("`clang`") using "`gcc`", but you can easily access the MacPorts GCC you just installed by using "`gcc-mp-8`" instead. *So when you are installing the software in the next sections, just use "`gcc-mp-8`" instead of "`gcc`" when you are asked to specify the C compiler.*
-
-You can check the version of your compiler (for example):
+If the installation succeeds, the SPIDER executable will be available at:
-```
-gcc --version
-gcc-mp-8 --version
+```bash
+./spider
```
-If you see a message about "Apple LLVM" then your compiler will not support quadruple precision. You can also test with this command (replace `gcc` by `gcc-mp-8` to test the MacPorts compiler):
+The PETSc environment used for the build will be reported by the installer, for example:
```
-echo '#include' > t.c && echo '#include' >> t.c && echo 'int main(){printf("It seems to work!\n");}' >> t.c && gcc t.c && ./a.out && rm -f t.c a.out
+PETSC_DIR=/somewhere/to/install/petsc
+PETSC_ARCH=arch-xxx-yyy
```
+!!! info "PETSc environment"
+ `./tools/get_spider.sh` sets `PETSC_DIR` and `PETSC_ARCH` automatically for installation. You only need to export them yourself if you want to rebuild or test SPIDER manually in a new shell session, like this:
-#### 2.2 Build Dependencies (Quadruple Precision)
-
-**For double precision, you can follow *Quick Installation* to install PETSc, which will also install SUNDIALs.**
-
-Before you begin, comment out any existing references to `PETSC_DIR` and `PETSC_ARCH` in your
-`.profile`, `.bash_profile`,`.bashrc`, etc. and clear these variables from your current terminal session.
-
-For quadruple precision, you must install SUNDIALS independently of PETSc, since we use a modified version of SUNDIALS to support quadruple precision.
-
-#### 2.2.1 Install SUNDIALS with quadruple precision
-
-1. Clone this specific, modified version of SUNDIALS from the Git repository:
-
- ```
- cd /somewhere/to/install
- mkdir src
- git clone https://bitbucket.org/psanan/sundials-quad src
- ```
-
-2. Make sure that you have CMake available by typing `cmake --version`. If this fails, then install CMake from your package manager (homebrew, macports, apt,..) or by following [the instructions from CMake](https://cmake.org/download). Note: it is necessary to comment out the `cmake_policy(SET CMP0042 NEW)` in `src/CMakeLists.txt` if you are using an old version of CMake.
-
- ```
- sudo port install cmake # MacPorts
- brew install cmake # Homebrew
- sudo apt-get install cmake # apt
- ```
-
-3. Configure, build, and install SUNDIALS:
-
- ```
- mkdir install build
- cd build
- cmake ../src
- ccmake . # with apt you may need to install this separately
- ```
-
-4. Use the `ccmake` interface to set values similar to those below.
-Make sure you type "c" to configure once you have entered these values, then "g" to generate and exit.
-Note: specify the same C compiler you used to install PETSc (probably `gcc`):
-
- ```
- CMAKE_C_COMPILER: gcc
- CMAKE_INSTALL_PREFIX: ../install
- EXAMPLES_INSTALL_PATH: ../install/examples
- SUNDIALS_PRECISION: quadruple
- ```
-
-5. Make and install:
-
- ```
- make && make install
- ```
-
-#### 2.2.2 Install PETSc with quadruple precision
-
-
-6. Download PETSc. Use this specific version as given below if interested in reproducing previous results, though any version of PETSc greater than or equal to 3.17 is expected to work just as well.
-
- ```
- cd /somewhere/to/install
- git clone https://gitlab.com/petsc/petsc -b main petsc-quad
- cd petsc-quad
- git checkout 63b725033a15f75ded7183cf5f88ec748e60783b
- ```
-
-7. Configure PETSc using the following command. Crucially, in the next step we point PETSc to the quadruple precision installation of SUNDIALS that we just created (change `/somewhere/to/install` to the place that you installed SUNDIALS):
-
- ```
- ./configure --with-debugging=0 --with-fc=0 --with-cxx=0 --with-cc=gcc --with-precision=__float128 --with-sundials=1 --with-sundials-dir=/somewhere/to/install/sundials-quad/install --download-mpich --download-f2cblaslapack --COPTFLAGS="-g -O3" --CXXOPTFLAGS="-g -O3"
- ```
-
-8. Follow the terminal instructions to complete the installation of PETSc. You can copy-paste the commands returned to you in the terminal window. Make note of `PETSC_DIR` and `PETSC_ARCH`, which are also reported in the terminal window.
-
-9. In your environment, set `PETSC_DIR` and `PETSC_ARCH` to the PETSc installation. `PETSC_ARCH` will look something like arch-xxx-yyy (e.g. arch-darwin-c-opt). This completes the setup of PETSc:
-
- ```
- export PETSC_DIR=/somewhere/to/install/petsc-quad
+ ```bash
+ export PETSC_DIR=/somewhere/to/install/petsc
export PETSC_ARCH=arch-xxx-yyy
```
-#### 2.2.3 Install SPIDER
-
-10. Now download SPIDER:
-
- ```
- cd /somewhere/to/install
- git clone https://github.com/FormingWorlds/SPIDER.git
- cd SPIDER
- ```
-
-11. Make SPIDER:
-
- ```
- make clean
- make -j
- ```
-
-12. Test basic functionality:
-
- ```
- make test
- ```
-
-You should now be ready to use the code!
\ No newline at end of file
diff --git a/docs/How-to/quadruple_installation.md b/docs/How-to/quadruple_installation.md
new file mode 100644
index 00000000..6e342bf6
--- /dev/null
+++ b/docs/How-to/quadruple_installation.md
@@ -0,0 +1,158 @@
+# Installation with quadruple precision
+
+Quadruple precision is useful for numerically demanding cases, but it is not the default installation path. To build SPIDER with quadruple precision, you need to install SUNDIALS and PETSc manually with quadruple-precision support first, and then build SPIDER against that installation.
+
+## What is different from the standard install?
+
+For quadruple precision:
+
+- you need a **real GCC compiler**, not Apple Clang on macOS;
+- you need a **separate quadruple-precision SUNDIALS build**;
+- you then configure PETSc with `--with-precision=__float128` and point it to that SUNDIALS installation;
+- finally, you build SPIDER with `PETSC_DIR` and `PETSC_ARCH` set to that PETSc build.
+
+## 1. Check that your compiler supports quadruple precision
+
+The SPIDER docs note that quadruple precision requires a GCC compiler. On macOS, `gcc` often points to Apple Clang, which does **not** provide GNU quadruple-precision support.
+
+Check your compiler:
+
+```bash
+gcc --version
+```
+
+If the output mentions **Apple LLVM**, install GCC separately and use that compiler binary instead.
+
+A simple test is:
+
+```bash
+echo '#include' > t.c && \
+echo '#include' >> t.c && \
+echo 'int main(){printf("It seems to work!\n");}' >> t.c && \
+gcc t.c && ./a.out && rm -f t.c a.out
+```
+
+If that fails, your current compiler is not suitable for the quadruple-precision build.
+
+## 2. Install SUNDIALS with quadruple precision
+
+For quadruple precision, SPIDER uses a modified SUNDIALS build rather than PETSc’s default downloaded copy. The documented workflow is: clone the modified SUNDIALS repository, configure it with CMake, and set `SUNDIALS_PRECISION=quadruple`.
+
+!!! warning "Check that CMake is available"
+ Make sure that CMake is available:
+ ```bash
+ cmake --version
+ ```
+ If it is not installed, follow the instructions [here](https://cmake.org/download/), or run:
+
+ === "MacPorts"
+
+ ```bash
+ sudo port install cmake
+ ```
+
+ === "Homebrew"
+
+ ```bash
+ brew install cmake
+ ```
+
+ === "Ubuntu / Debian"
+
+ ```bash
+ sudo apt-get install cmake
+ ```
+
+```bash
+cd /somewhere/to/install
+mkdir -p sundials-quad
+cd sundials-quad
+
+git clone https://bitbucket.org/psanan/sundials-quad src
+mkdir install build
+cd build
+cmake ../src
+ccmake .
+```
+
+In `ccmake`, set values similar to:
+
+!!! note "Using the ccmake interface"
+ When using the `ccmake` interface, make sure you type "c" to configure once you have entered these values, then "g" to generate and exit.
+
+```
+CMAKE_C_COMPILER: gcc
+CMAKE_INSTALL_PREFIX: ../install
+EXAMPLES_INSTALL_PATH: ../install/examples
+SUNDIALS_PRECISION: quadruple
+```
+
+Then build and install:
+
+```
+make && make install
+```
+
+!!! note "C compiler"
+ Use the same C compiler here that you will use for PETSc.
+
+## 3. Install PETSc with quadruple precision
+
+Install the pinned snapshot if interested in reproducing previous results. Otherwise, install PETSc 3.19.
+
+=== "Pinned SPIDER-documented commit"
+
+ ```bash
+ cd /somewhere/to/install
+ git clone https://gitlab.com/petsc/petsc -b main petsc-quad
+ cd petsc-quad
+ git checkout 63b725033a15f75ded7183cf5f88ec748e60783b
+ ```
+
+=== "PETSc 3.19"
+
+ ```bash
+ cd /somewhere/to/install
+ git clone https://gitlab.com/petsc/petsc petsc-3.19
+ cd petsc-3.19
+ git checkout v3.19.6
+ ```
+
+Configure it against the SUNDIALS installation you just built:
+
+```bash
+./configure \
+ --with-debugging=0 \
+ --with-fc=0 \
+ --with-cxx=0 \
+ --with-cc=gcc \
+ --with-precision=__float128 \
+ --with-sundials=1 \
+ --with-sundials-dir=/somewhere/to/install/sundials-quad/install \
+ --download-mpich \
+ --download-f2cblaslapack \
+ --COPTFLAGS="-g -O3" \
+ --CXXOPTFLAGS="-g -O3"
+```
+
+Then follow PETSc’s terminal instructions to complete the build, and export the environment it reports:
+
+```bash
+export PETSC_DIR=/somewhere/to/install/petsc-quad
+export PETSC_ARCH=arch-xxx-yyy
+```
+
+## 4. Build SPIDER against the quadruple-precision PETSc
+
+Once `PETSC_DIR` and `PETSC_ARCH` point to your quadruple-precision PETSc build, build SPIDER normally:
+
+```bash
+cd /somewhere/to/install
+git clone https://github.com/FormingWorlds/SPIDER.git
+cd SPIDER
+
+make clean
+make -j
+```
+
+Then test your installation by following the steps in the [testing guide](test.md).
\ No newline at end of file
diff --git a/docs/How-to/test.md b/docs/How-to/test.md
new file mode 100644
index 00000000..106740a4
--- /dev/null
+++ b/docs/How-to/test.md
@@ -0,0 +1,134 @@
+# Testing SPIDER
+
+This guide explains how to validate your SPIDER installation using the test suite.
+
+!!! info "Prerequisites"
+ - SPIDER is compiled and the `spider` executable exists in the project root.
+ - PETSc is installed.
+ - Python 3.12 is available (preferably in a Conda environment).
+
+## 1. Install SciATH
+
+Get SciATH (Scientific Application Test Harness), which is a Python module:
+
+```bash
+cd /somewhere/to/install
+git clone https://github.com/sciath/sciath -b dev
+```
+
+Add the module to your Python path:
+
+```bash
+export PYTHONPATH=$PYTHONPATH:/path/to/sciath
+```
+
+## 2. Set `PETSC_DIR` and `PETSC_ARCH`
+
+`PETSC_DIR` and `PETSC_ARCH` are not automatically set after installing SPIDER. Set them manually:
+
+```bash
+export PETSC_DIR=/somewhere/to/install/petsc
+export PETSC_ARCH=arch-xxx-yyy
+```
+
+!!! info "What to set for `PETSC_DIR` and `PETSC_ARCH?`"
+ The SPIDER installer automatically reports the `PETSC_ARCH` and `PETSC_DIR` that PETSc was built against. To see what you need to set for these variables, run the installer again:
+ ```bash
+ ./tools/get_spider.sh
+ ```
+
+## 3. Test
+
+From the SPIDER root directory, test SPIDER's basic functionality:
+
+```bash
+make test
+```
+
+This command:
+
+1. Creates a `test_dir/` output directory.
+2. Runs the test harness on the suite defined in `tests/tests.yml`.
+3. Reports the location of the test output and a test report by SciATH.
+
+All test outputs are collected in `test_dir/`. After tests complete, you can find:
+
+```
+test_dir/
+ - pth.conf # Configuration file for test harness
+ - blackbody50_output/ # Output from standard test
+ - external_mesh_roundtrip_output/ # External mesh validation
+ - non_aw_mesh_output/ # Non-AW mesh validation
+ - plot_test_output/ # Plotting test output
+ - sciath_test_report.txt # SciATH test report
+```
+
+## What the test suite validates
+
+The test suite (defined in `tests/tests.yml`) runs several checks:
+
+| Test | Purpose |
+|------|---------|
+| `blackbody50` | Core interior dynamics on a Earth-like blackbody planet. Final state is compared against known outputs. Check `tests/opts/blackbody50.opt` for exact configuration.|
+| `plot_test` | Validates the Python plotting script (`py/plot_spider_lite.py`) runs without error. |
+| `external_mesh_roundtrip` | Verifies SPIDER accepts external mesh files and produces correct results. |
+| `non_aw_mesh` | Confirms SPIDER works with non-Adams-Williamson density profiles. |
+
+For setup details and file format requirements, see [External Mesh Input](external_mesh_input.md).
+
+Each test runs one or more commands and compares output against expected values with specified tolerances.
+
+## Common test issues
+
+### SciATH not found
+
+```
+Error: sciath module not found
+```
+
+**Solution:**
+Ensure SciATH is cloned and added to `PYTHONPATH`:
+
+```bash
+export PYTHONPATH=$PYTHONPATH:/path/to/sciath
+make test
+```
+
+### Tolerance mismatches
+
+If a test fails with a tolerance error (e.g., relative tolerance exceeded), it usually indicates:
+
+- A code change that alters output slightly (check your recent commits).
+- Different compiler flags or PETSc version (some tests have tight tolerances). Note that by default, PETSc version 3.19.0 is installed, and newer versions might lead to tolerance errors.
+- Differences in floating-point rounding between systems.
+
+Most tests have `rtol: 1e-5` (relative tolerance) and `atol: 1e-5` (absolute tolerance). See `tests/tests.yml` for specific thresholds.
+
+### Plot test fails
+
+The `plot_test` checks that the Python plotting script runs without error. If it fails, plot the test output manually and see what is going wrong:
+
+```bash
+python py/plot_spider_lite.py -d test_dir/blackbody50_output/sandbox/output
+```
+
+This generates a file called `interior.pdf` into a `plots/` directory.
+
+If need, install missing dependencies:
+
+```bash
+pip install -r py/requirements.txt
+```
+
+## Comparing against expected output
+
+After a test run completes, you can manually compare your output against known good results. A quick way to check everything worked well, is by comparing plots. If you have VS Code's `code` installed:
+
+```bash
+code test_dir/plot_test_output/sandbox/plots/interior.pdf
+code tests/expected_output/blackbody50-interior.png
+```
+
+or open the files manually.
+
+Expected files are in `tests/expected_output/`. Small differences due to compiler or system differences are usually acceptable if they are within the specified tolerances.
\ No newline at end of file
diff --git a/docs/Tutorials/first_run.md b/docs/Tutorials/first_run.md
new file mode 100644
index 00000000..8d7b074a
--- /dev/null
+++ b/docs/Tutorials/first_run.md
@@ -0,0 +1,57 @@
+# Tutorial: first SPIDER run
+
+This page walks you through a minimal SPIDER run, from compile to quick visualization.
+
+!!! info "Prerequisites"
+ - You have installed PETSc and SPIDER by going through the [installation guide](../How-to/installation.md).
+ - You have a working C compiler.
+ - Preferably: you are in a Conda environment with python version 3.12.
+
+## 1. Run a known example
+
+Use one of the provided options files:
+
+```bash
+./spider -options_file tests/opts/blackbody50.opts
+```
+
+This writes model output to the default output directory (typically `output/`).
+
+!!! info "Runtime"
+ A first run on a laptop/workstation typically finishes in seconds to a few minutes, depending on CPU and PETSc settings.
+
+To quickly verify output structure:
+
+```bash
+ls output
+```
+You should see text files with radial profiles and time-series data.
+
+Expected signs of success:
+
+- `output` exists and is non-empty.
+- Multiple text files are produced (for example, interior profile and time-series outputs).
+- No PETSc crash/solver error is printed to terminal.
+
+## 2. Make a quick plot
+
+```bash
+python py/plot_spider_lite.py -h
+python py/plot_spider_lite.py -d output
+```
+
+The plotting script generates a basic figure of interior profiles from your run output inside the directory `plots/`.
+
+## 3. Next steps
+
+- Try a different options file in `tests/opts/`.
+- Compare your run output against files in `tests/expected_output/`.
+
+## Common first-run issues
+
+- `PETSC_DIR` or `PETSC_ARCH` not set:
+ Re-export them in your shell and rebuild.
+- `./spider` not found:
+ Build failed or you are not in the project root.
+- Plot script fails:
+ Install Python dependencies from `py/requirements.txt`.
\ No newline at end of file
diff --git a/docs/assets/schematic_round.png b/docs/assets/schematic_round.png
new file mode 100644
index 00000000..f5033381
Binary files /dev/null and b/docs/assets/schematic_round.png differ
diff --git a/docs/getting_started.md b/docs/getting_started.md
new file mode 100644
index 00000000..7004a181
--- /dev/null
+++ b/docs/getting_started.md
@@ -0,0 +1,60 @@
+# Getting started
+
+!!! note "Usage within the PROTEUS framework"
+ SPIDER is most commonly installed and used as integrated into the PROTEUS framework. Understand how to use PROTEUS [here](https://proteus-framework.org/PROTEUS).
+
+## Quick path
+
+Here is the quickest path to getting started:
+
+1. **Install SPIDER**
+ Follow the installation steps.
+ → [Installation guide](How-to/installation.md)
+
+2. **Test SPIDER**
+ Test the basic functionality of SPIDER.
+ → [Test guide](How-to/test.md)
+
+3. **Run your first model**
+ Follow a minimal example run and generate a quick plot.
+ → [First run tutorial](Tutorials/first_run.md)
+
+---
+
+## What do you want to do?
+
+
+
+- :material-download: **Install**
+
+ [Go to installation guide](How-to/installation.md)
+
+- :material-test-tube: **Test SPIDER**
+
+ [Go to testing guide](How-to/test.md)
+
+- :material-rocket-launch: **Run SPIDER**
+
+ [Go to tutorials](Tutorials/first_run.md)
+
+- :material-book-open-variant: **Understand the model**
+
+ [Go to model overview](Explanations/basic_thermodynamics.md)
+
+- :material-bookshelf: **Check SPIDER publications**
+
+ [Go to publications](Reference/publications.md)
+
+- :material-github: **Contribute / browse code**
+
+ [Go to source code](https://github.com/FormingWorlds/SPIDER)
+
+- :material-bug: **Raise an issue**
+
+ [Go to issues](https://github.com/FormingWorlds/SPIDER/issues)
+
+- :material-email: **Get in touch**
+
+ [Go to contact](Community/contact.md)
+
+
\ No newline at end of file
diff --git a/docs/index.md b/docs/index.md
index 7a025b5a..33a371ab 100644
--- a/docs/index.md
+++ b/docs/index.md
@@ -18,15 +18,7 @@
SPIDER is a 1-D parameterised interior dynamics code for rocky planets with molten and/or solid interiors and support for volatile cycling, redox reactions, and radiative transfer in the atmosphere.
!!! note
- This documentation describes the version of SPIDER as part of the [PROTEUS Framework](https://proteus-framework.org/proteus). For the original SPIDER code, see [djbower/SPIDER](https://github.com/djbower/spider).
-
-## Getting started
-
-- [Installation guide](How-to/installation.md)
-- [Publications](Reference/publications.md)
-- [Model overview](Explanations/model.md)
-- [Source code](https://github.com/FormingWorlds/SPIDER)
-- [Contact](Community/contact.md)
+ This documentation describes the version of SPIDER as part of the [PROTEUS Framework](https://proteus-framework.org/PROTEUS). For the original SPIDER code, see [djbower/SPIDER](https://github.com/djbower/spider).
If you plan to contribute to SPIDER, please read our [Code of Conduct](Community/CODE_OF_CONDUCT.md).
If you are running into problems, please do not hesitate to raise an [Issue](https://github.com/FormingWorlds/SPIDER/issues).
diff --git a/docs/proteus_framework.md b/docs/proteus_framework.md
new file mode 100644
index 00000000..d0f3c6a1
--- /dev/null
+++ b/docs/proteus_framework.md
@@ -0,0 +1,20 @@
+
+
+SPIDER is the interior evolution module of PROTEUS (/ˈproʊtiəs, PROH-tee-əs), a modular Python framework that simulates the coupled evolution of the atmospheres and interiors of rocky planets and exoplanets. A schematic of PROTEUS components and corresponding modules can be found below.
+
+
+You can find the documentation of each PROTEUS module in the sidebar.
+
+
+
+
+ 
+ Schematic of PROTEUS components and corresponding modules.
+
\ No newline at end of file
diff --git a/docs/refs.bib b/docs/refs.bib
index 06fa6b46..85dff414 100644
--- a/docs/refs.bib
+++ b/docs/refs.bib
@@ -20869,7 +20869,7 @@ @Book{DM62
title = {Non-Equilibrium Thermodynamics},
publisher = {North-Holland Publishing Company, Amsterdam},
year = {1962},
- author = {S. R. {De~Groot} and P. Mazur},
+ author = {S. R. De Groot and P. Mazur},
}
@Article{CGH00,
diff --git a/docs/stylesheets/extra.css b/docs/stylesheets/extra.css
index 29aed185..83c5b8f0 100644
--- a/docs/stylesheets/extra.css
+++ b/docs/stylesheets/extra.css
@@ -1,40 +1,250 @@
-/* Preserve line breaks when docstrings render as plain paragraphs */
-.doc-contents p {
- white-space: pre-wrap;
+/* =========================================================
+ PROTEUS theme variables
+ ========================================================= */
+
+/* Be careful when changing these, as they affect multiple elements across the site. */
+
+[data-md-color-scheme="default"],
+[data-md-color-scheme="slate"] {
+ --md-primary-fg-color: #1c2b4b;
+ --md-primary-fg-color--light: #2a3d69;
+ --md-primary-fg-color--dark: #14203a;
+
+ --proteus-highlight-color: #ff6e40;
+ --proteus-highlight-bg-soft: rgba(255, 109, 64, 0.093);
+
+ --md-accent-fg-color: var(--proteus-highlight-color);
+
+ --md-typeset-a-color: #3b6193;
+}
+
+/* Softer slate background + more muted link color */
+[data-md-color-scheme="slate"] {
+ --md-default-bg-color: #0f172ad2;
+ --md-default-bg-color--light: #1e293b;
+ --md-default-bg-color--lighter: #334155;
+ --md-default-bg-color--lightest: #475569;
+
+ --md-typeset-a-color: #8fa8c9;
+}
+
+/* =========================================================
+ Header + tabs
+ ========================================================= */
+
+[data-md-color-scheme="default"] .md-header,
+[data-md-color-scheme="default"] .md-tabs,
+[data-md-color-scheme="slate"] .md-header,
+[data-md-color-scheme="slate"] .md-tabs {
+ background-color: var(--md-primary-fg-color);
+}
+
+[data-md-color-scheme="default"] .md-header *,
+[data-md-color-scheme="default"] .md-tabs *,
+[data-md-color-scheme="slate"] .md-header *,
+[data-md-color-scheme="slate"] .md-tabs * {
+ color: #fff !important;
+ fill: #fff !important;
+}
+
+/* =========================================================
+ Expanded search
+ ========================================================= */
+
+[data-md-color-scheme="default"] .md-search__form,
+[data-md-color-scheme="slate"] .md-search__form {
+ background-color: rgba(255, 255, 255, 0.12) !important;
+ border-radius: 0.2rem !important;
+ box-shadow: none !important;
+}
+
+[data-md-color-scheme="default"] .md-search__form:hover,
+[data-md-color-scheme="default"] .md-search__form:focus-within,
+[data-md-color-scheme="slate"] .md-search__form:hover,
+[data-md-color-scheme="slate"] .md-search__form:focus-within {
+ background-color: rgba(255, 255, 255, 0.16) !important;
+}
+
+[data-md-color-scheme="default"] .md-search__input,
+[data-md-color-scheme="slate"] .md-search__input {
+ color: #fff !important;
+ -webkit-text-fill-color: #fff !important;
+ caret-color: #fff !important;
+ background: transparent !important;
+ -webkit-appearance: none;
+ appearance: none;
+}
+
+[data-md-color-scheme="default"] .md-search__input::placeholder,
+[data-md-color-scheme="slate"] .md-search__input::placeholder {
+ color: rgba(255, 255, 255, 0.75) !important;
+ -webkit-text-fill-color: rgba(255, 255, 255, 0.75) !important;
+ opacity: 1 !important;
+}
+
+/* Hide browser-native search decorations */
+.md-search__input::-webkit-search-decoration,
+.md-search__input::-webkit-search-cancel-button,
+.md-search__input::-webkit-search-results-button,
+.md-search__input::-webkit-search-results-decoration {
+ -webkit-appearance: none;
+ appearance: none;
+ display: none;
+}
+
+/* Expanded search icons */
+[data-md-color-scheme="default"] .md-search__icon,
+[data-md-color-scheme="default"] .md-search__icon svg,
+[data-md-color-scheme="default"] .md-search__icon svg *,
+[data-md-color-scheme="default"] .md-search__label,
+[data-md-color-scheme="default"] .md-search__label svg,
+[data-md-color-scheme="default"] .md-search__label svg *,
+[data-md-color-scheme="slate"] .md-search__icon,
+[data-md-color-scheme="slate"] .md-search__icon svg,
+[data-md-color-scheme="slate"] .md-search__icon svg *,
+[data-md-color-scheme="slate"] .md-search__label,
+[data-md-color-scheme="slate"] .md-search__label svg,
+[data-md-color-scheme="slate"] .md-search__label svg * {
+ color: #fff !important;
+ fill: #fff !important;
+ stroke: #fff !important;
+ opacity: 1 !important;
+}
+
+/* =========================================================
+ Collapsed search trigger in header
+ ========================================================= */
+
+[data-md-color-scheme="default"] .md-search__button,
+[data-md-color-scheme="slate"] .md-search__button {
+ color: #fff !important;
+ background-color: rgba(255, 255, 255, 0.12) !important;
+ border-radius: 0.6rem !important;
}
-/* Wrap long identifiers instead of overflowing */
-.doc-contents,
-.doc-contents * {
- overflow-wrap: anywhere;
+[data-md-color-scheme="default"] .md-search__button:hover,
+[data-md-color-scheme="default"] .md-search__button:focus,
+[data-md-color-scheme="slate"] .md-search__button:hover,
+[data-md-color-scheme="slate"] .md-search__button:focus {
+ background-color: rgba(255, 255, 255, 0.16) !important;
}
-/* ---- Footnote/citation markers: render as inline [1] instead of superscript ---- */
-.md-typeset sup[id^="fnref"] {
- vertical-align: baseline !important;
- font-size: 1em !important;
- line-height: inherit !important;
+/* Collapsed magnifier */
+[data-md-color-scheme="default"] .md-search__button::before,
+[data-md-color-scheme="slate"] .md-search__button::before {
+ color: #fff !important;
+ -webkit-text-fill-color: #fff !important;
+ filter: brightness(0) invert(1) !important;
+ opacity: 1 !important;
}
-.md-typeset sup[id^="fnref"] > a.footnote-ref {
- text-decoration: none;
+/* If an inner SVG is used in some states */
+[data-md-color-scheme="default"] .md-search__button svg,
+[data-md-color-scheme="default"] .md-search__button svg *,
+[data-md-color-scheme="slate"] .md-search__button svg,
+[data-md-color-scheme="slate"] .md-search__button svg * {
+ fill: #fff !important;
+ stroke: #fff !important;
+ color: #fff !important;
}
-.md-typeset sup[id^="fnref"] > a.footnote-ref::before { content: "["; }
-.md-typeset sup[id^="fnref"] > a.footnote-ref::after { content: "]"; }
+/* Shortcut badge */
+[data-md-color-scheme="default"] .md-search__button kbd,
+[data-md-color-scheme="default"] .md-search__button .md-search__kbd,
+[data-md-color-scheme="slate"] .md-search__button kbd,
+[data-md-color-scheme="slate"] .md-search__button .md-search__kbd {
+ color: rgba(255, 255, 255, 0.9) !important;
+ background-color: rgba(255, 255, 255, 0.18) !important;
+ border: none !important;
+ box-shadow: none !important;
+}
+
+/* Badge drawn as pseudo-element in some versions */
+[data-md-color-scheme="default"] .md-search__button::after,
+[data-md-color-scheme="slate"] .md-search__button::after {
+ background-color: rgba(255, 255, 255, 0.12) !important;
+ border: none !important;
+ box-shadow: none !important;
+ color: rgba(255, 255, 255, 0.9) !important;
+}
+
+/* =========================================================
+ Top tabs
+ ========================================================= */
+
+/* All tab labels white by default */
+[data-md-color-scheme="default"] .md-tabs__link,
+[data-md-color-scheme="slate"] .md-tabs__link {
+ color: #fff !important;
+ opacity: 0.9 !important;
+ transition: color 0.15s ease, opacity 0.15s ease !important;
+}
-/* fallback (some versions/themes) */
-.md-typeset a.footnote-ref {
- vertical-align: baseline !important;
- font-size: 1em !important;
+/* Hover state */
+[data-md-color-scheme="default"] .md-tabs__link:hover,
+[data-md-color-scheme="slate"] .md-tabs__link:hover {
+ color: var(--proteus-highlight-color) !important;
+ opacity: 0.8 !important;
}
-/* reduce space under the page title */
-.md-typeset h1 {
- margin-bottom: 0.2rem; /* try 0, 0.2rem, 0.5rem */
+/* Active tab text only, no underline */
+[data-md-color-scheme="default"] .md-tabs__item--active,
+[data-md-color-scheme="default"] .md-tabs__link--active,
+[data-md-color-scheme="default"] .md-tabs__item--active .md-tabs__link,
+[data-md-color-scheme="slate"] .md-tabs__item--active,
+[data-md-color-scheme="slate"] .md-tabs__link--active,
+[data-md-color-scheme="slate"] .md-tabs__item--active .md-tabs__link {
+ color: var(--proteus-highlight-color) !important;
+ box-shadow: none !important;
+ border-bottom: none !important;
+ text-decoration: none !important;
+}
+
+/* Remove any underline/pseudo-element indicator */
+[data-md-color-scheme="default"] .md-tabs__item--active::after,
+[data-md-color-scheme="default"] .md-tabs__link--active::after,
+[data-md-color-scheme="default"] .md-tabs__item--active .md-tabs__link::after,
+[data-md-color-scheme="slate"] .md-tabs__item--active::after,
+[data-md-color-scheme="slate"] .md-tabs__link--active::after,
+[data-md-color-scheme="slate"] .md-tabs__item--active .md-tabs__link::after {
+ content: none !important;
+ display: none !important;
+ background: none !important;
+}
+
+/* =========================================================
+ Sidebar navigation
+ ========================================================= */
+
+/* Style only active leaf page links */
+[data-md-color-scheme="default"] .md-nav__item .md-nav__link--active:not(.md-nav__link--passed),
+[data-md-color-scheme="slate"] .md-nav__item .md-nav__link--active:not(.md-nav__link--passed) {
+ background-color: var(--proteus-highlight-bg-soft) !important;
+ border-radius: 1rem !important;
+ color: var(--proteus-highlight-color) !important;
+ padding-left: 1rem;
+ padding-right: 1rem;
+}
+
+/* =========================================================
+ Footer
+ ========================================================= */
+
+/* Remove underline from footer copyright link */
+.md-footer-copyright a,
+.md-footer-meta a {
+ text-decoration: none !important;
+}
+
+/* =========================================================
+ Make header title look clickable and add hover effect
+ ========================================================= */
+
+.md-header__title[data-md-component="header-title"] {
+ cursor: pointer;
+ transition: opacity 0.15s ease !important;
}
-/* optionally reduce any top margin on the first image */
-.md-typeset h1 + p img {
- margin-top: 0;
+.md-header__title[data-md-component="header-title"]:hover {
+ opacity: 0.7 !important;
}
diff --git a/docs/stylesheets/footnotes.css b/docs/stylesheets/footnotes.css
new file mode 100644
index 00000000..f3729cf1
--- /dev/null
+++ b/docs/stylesheets/footnotes.css
@@ -0,0 +1,29 @@
+/* ---- Footnote/citation markers: render as inline [1] instead of superscript ---- */
+.md-typeset sup[id^="fnref"] {
+ vertical-align: baseline !important;
+ font-size: 1em !important;
+ line-height: inherit !important;
+}
+
+.md-typeset sup[id^="fnref"] > a.footnote-ref {
+ text-decoration: none;
+}
+
+.md-typeset sup[id^="fnref"] > a.footnote-ref::before { content: "["; }
+.md-typeset sup[id^="fnref"] > a.footnote-ref::after { content: "]"; }
+
+/* fallback (some versions/themes) */
+.md-typeset a.footnote-ref {
+ vertical-align: baseline !important;
+ font-size: 1em !important;
+}
+
+/* reduce space under the page title */
+.md-typeset h1 {
+ margin-bottom: 0.2rem; /* try 0, 0.2rem, 0.5rem */
+}
+
+/* optionally reduce any top margin on the first image */
+.md-typeset h1 + p img {
+ margin-top: 0;
+}
diff --git a/mkdocs.yml b/mkdocs.yml
index ef219825..76435368 100644
--- a/mkdocs.yml
+++ b/mkdocs.yml
@@ -3,26 +3,50 @@ site_url: https://proteus-framework.org/SPIDER/
repo_url: https://github.com/FormingWorlds/SPIDER
repo_name: GitHub
+copyright: '© 2023-2026 Forming Worlds Lab'
+
nav:
- - Home: index.md
+ - Home:
+ - About: index.md
+ - Getting started: getting_started.md
+
- How-to guides:
- - Installation: How-to/installation.md
+ - Installation:
+ - Quick installation: How-to/installation.md
+ - Quadruple precision: How-to/quadruple_installation.md
+ - Testing: How-to/test.md
+ - External mesh input: How-to/external_mesh_input.md
+
+ - Tutorials:
+ - First run: Tutorials/first_run.md
- Explanations:
- - Model overview: Explanations/model.md
+ - Model overview:
+ - Thermodynamics: Explanations/basic_thermodynamics.md
+ - Mass conservation and energy transport: Explanations/transport.md
+ - Material properties: Explanations/material_properties.md
+ - Mass coordinates: Explanations/mass_coordinates.md
+ - Parameterised fluxes: Explanations/parameterised_fluxes.md
+ - Phase separation and thermal evolution (Abe 1993): Explanations/abe1993.md
+ - Internal heating: Explanations/internal_heating.md
+ - Atmosphere: Explanations/atmosphere.md
+ - Boundary conditions: Explanations/boundary_conditions.md
+ - Hybrid equation of state: Explanations/hybrid_eos.md
+ - Toy model and numerical schemes: Explanations/toy_model.md
- Reference:
- Publications: Reference/publications.md
- Community:
- Code of Conduct: Community/CODE_OF_CONDUCT.md
- - Developers: https://proteus-framework.org/people
- Contact: Community/contact.md
+ - Developers: https://proteus-framework.org/people
+ - Issues: https://github.com/FormingWorlds/SPIDER/issues
- Source code: https://github.com/FormingWorlds/SPIDER
- - Issues page: https://github.com/FormingWorlds/SPIDER/issues
- Other PROTEUS modules:
+ - PROTEUS submodules: proteus_framework.md
- 🔗 PROTEUS: https://proteus-framework.org/PROTEUS/
- 🔗 MORS: https://proteus-framework.org/MORS/
- 🔗 JANUS: https://proteus-framework.org/JANUS/
@@ -36,13 +60,17 @@ nav:
- 🔗 Atmodeller: https://atmodeller.readthedocs.io/en/latest/
- 🔗 FastChem: https://newstrangeworlds.github.io/FastChem/
- 🔗 PLATON: https://platon.readthedocs.io/en/latest/
+ - 🔗 SOCRATES: https://github.com/FormingWorlds/SOCRATES/
theme:
name: material
custom_dir: docs/overrides
features:
- - content.code.copy
- - navigation.expand
+ - navigation.tabs
+ - navigation.tabs.sticky
+ - navigation.expand
+ - content.code.copy
+ - content.tabs.link
# Default assets (used unless overridden per palette below)
favicon: assets/PROTEUS_black_on_white_logo_only.png
@@ -52,12 +80,12 @@ theme:
# Auto: follow system preference (and show a toggle)
- media: "(prefers-color-scheme: light)"
scheme: default
- primary: black
- accent: deep orange
+ primary: custom
+ accent: custom
favicon: assets/PROTEUS_black_on_white_logo_only.png
logo: assets/PROTEUS_white_on_black.png
toggle:
- icon: material/weather-night
+ icon: lucide/moon
name: Switch to dark mode
- media: "(prefers-color-scheme: dark)"
@@ -67,11 +95,12 @@ theme:
favicon: assets/PROTEUS_white_on_black_logo_only.png
logo: assets/PROTEUS_white_on_black.png
toggle:
- icon: material/weather-sunny
+ icon: lucide/sun
name: Switch to light mode
extra_css:
- stylesheets/extra.css
+ - stylesheets/footnotes.css
markdown_extensions:
- admonition
@@ -81,12 +110,37 @@ markdown_extensions:
- pymdownx.extra
- pymdownx.arithmatex:
generic: true
+ - pymdownx.superfences
+ - pymdownx.tabbed:
+ alternate_style: true
+ - pymdownx.details
+ - md_in_html
+ - pymdownx.emoji:
+ emoji_index: !!python/name:material.extensions.emoji.twemoji
+ emoji_generator: !!python/name:material.extensions.emoji.to_svg
- footnotes
extra_javascript:
- - https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js
- javascripts/header-links.js
+ - https://cdn.jsdelivr.net/npm/mathjax@3/es5/tex-mml-chtml.js
+# footer:
+extra:
+ generator: false
+ social:
+ - icon: material/web
+ link: https://proteus-framework.org/
+ name: PROTEUS website
+ - icon: material/email
+ link: mailto:proteus_dev@formingworlds.space
+ name: Mail PROTEUS developers
+ - icon: fontawesome/brands/python
+ link: https://pypi.org/project/fwl-proteus/
+ name: PROTEUS on PyPI
+ - icon: fontawesome/brands/github
+ link: https://github.com/FormingWorlds/PROTEUS
+ name: PROTEUS on GitHub
+
plugins:
- search
- tags
diff --git a/py/citations.py b/py/citations.py
new file mode 100644
index 00000000..4928331a
--- /dev/null
+++ b/py/citations.py
@@ -0,0 +1,94 @@
+import re
+from pathlib import Path
+try:
+ import bibtexparser
+except ImportError as exc:
+ raise SystemExit(
+ "The 'bibtexparser' package is required to run py/citations.py. "
+ "Please install it (e.g., with 'pip install bibtexparser') and "
+ "ensure it is listed in py/requirements.txt."
+ ) from exc
+
+DOCS_DIR = Path("docs")
+BIB_FILE = DOCS_DIR / "refs.bib"
+
+CITE_PATTERN = re.compile(r"\[@([A-Za-z0-9:_-]+)([^\]]*)\]")
+
+def clean_tex(s):
+ if not s:
+ return ""
+ return (
+ s.replace("{", "")
+ .replace("}", "")
+ .replace("~", " ")
+ .replace("\\&", "&")
+ .strip()
+ )
+
+def format_authors(author_field):
+ if not author_field:
+ return ""
+ authors = [clean_tex(a.strip()) for a in author_field.split(" and ")]
+ return "; ".join(authors)
+
+def format_entry(entry):
+ authors = format_authors(entry.get("author", ""))
+ title = clean_tex(entry.get("title", "Untitled"))
+ year = clean_tex(entry.get("year", "n.d."))
+ journal = clean_tex(
+ entry.get("journal") or entry.get("booktitle") or entry.get("publisher") or ""
+ )
+ address = clean_tex(entry.get("address", ""))
+
+ parts = [authors, f"*{title}*", journal, address, year]
+ return ", ".join(p for p in parts if p) + "."
+
+def main():
+ with open(BIB_FILE, "r", encoding="utf-8") as f:
+ bib_db = bibtexparser.load(f)
+
+ bib_map = {e["ID"]: format_entry(e) for e in bib_db.entries if "ID" in e}
+
+ for md_file in DOCS_DIR.rglob("*.md"):
+ text = md_file.read_text(encoding="utf-8")
+
+ # Skip files that do not contain raw citation syntax.
+ if not CITE_PATTERN.search(text):
+ print(f"Skipped {md_file} (no [@...] citations found)")
+ continue
+
+ used = {}
+
+ def repl(match):
+ key = match.group(1)
+ suffix = match.group(2).strip()
+ used[key] = bib_map.get(key, f"Missing bibliography entry for {key}.")
+ if suffix:
+ return f"[^cite-{key}], {suffix}"
+ return f"[^cite-{key}]"
+
+ new_text = CITE_PATTERN.sub(repl, text)
+
+ if used:
+ footnotes = "\n".join(
+ f"[^cite-{key}]: {value}" for key, value in used.items()
+ )
+
+ # Remove an existing \bibliography line if present
+ new_text = re.sub(r"(?m)^\s*\\bibliography\s*$\n?", "", new_text)
+
+ # Remove any old generated cite footnotes before appending fresh ones
+ new_text = re.sub(
+ r"(?m)^\[\^cite-[A-Za-z0-9:_-]+\]:\s.*(?:\n(?!\[\^cite-).*)*",
+ "",
+ new_text
+ ).rstrip()
+
+ new_text += "\n\n" + footnotes + "\n"
+
+ md_file.write_text(new_text, encoding="utf-8")
+ print(f"Processed {md_file}")
+
+
+if __name__ == "__main__":
+ main()
\ No newline at end of file
diff --git a/py/plot_spider_lite.py b/py/plot_spider_lite.py
index 602f3d0f..859896bb 100755
--- a/py/plot_spider_lite.py
+++ b/py/plot_spider_lite.py
@@ -445,7 +445,7 @@ def set_xaxis_from_kwargs(ax, myjson_o=None, **kwargs):
ax.set_xlim(np.min(xticks), np.max(xticks))
ax.invert_xaxis()
ax.xaxis.set_major_locator(ticker.FixedLocator(xticks))
- ax.set_xlabel("Melt fraction (\%)")
+ ax.set_xlabel(r"Melt fraction (%)")
# ====================================================================
@@ -482,7 +482,11 @@ def figure_interior(indir="output", time=None):
labels = ("{:.2e}".format(time) for time in myjson_o.time_l)
axs[1][1].legend(handles, labels, ncol=2, title="Time (yrs)")
- fig.savefig("interior.pdf")
+ # create output directory if it does not exist
+ os.makedirs("plots", exist_ok=True)
+
+ # save figure in plots directory
+ fig.savefig(os.path.join("plots", "interior.pdf"))
# ===================================================================
diff --git a/rhs.c b/rhs.c
index b20e7b16..4907c030 100644
--- a/rhs.c
+++ b/rhs.c
@@ -20,6 +20,7 @@ PetscErrorCode RHSFunction(TS ts,PetscReal t,Vec sol_in,Vec rhs,void *ptr)
Solution *S = &E->solution;
PetscScalar *arr_dSdt_s, *arr_rhs_b;
const PetscScalar *arr_Etot, *arr_capacitance_s, *arr_temp_s, *arr_cp_s, *arr_Htot_s, *arr_xi_s;
+ PetscScalar dSdt_s0;
PetscMPIInt rank,size;
DM da_s = E->da_s, da_b=E->da_b;
PetscInt i,v,ihi_s,ilo_s,w_s,numpts_b;
@@ -60,7 +61,7 @@ PetscErrorCode RHSFunction(TS ts,PetscReal t,Vec sol_in,Vec rhs,void *ptr)
arr_dSdt_s[0] = ( arr_Etot[1] - arr_Etot[0] ) / arr_capacitance_s[0];
arr_dSdt_s[0] += arr_Htot_s[0] / arr_temp_s[0];
- /* dSdt at staggered nodes and d/dt(dS/dr) at internal basic nodes */
+ /* dSdt at staggered nodes and d/dt(dS/dxi) at internal basic nodes */
for(i=ilo_s+1; idtsurfdt already contains contribution of dTsurf/dT */
/* By chain rule, just need dT/dt */
- A->dtsurfdt *= arr_dSdt_s[0] * arr_temp_s[0] / arr_cp_s[0];
+ A->dtsurfdt *= dSdt_s0 * arr_temp_s[0] / arr_cp_s[0];
/* add effect of gradient to above 0.5*d/dt (dS/dxi)? */
ierr = DMDAVecRestoreArrayRead(da_s,M->xi_s,&arr_xi_s);CHKERRQ(ierr);
@@ -92,7 +96,7 @@ PetscErrorCode RHSFunction(TS ts,PetscReal t,Vec sol_in,Vec rhs,void *ptr)
/* apply cmb boundary condition to rhs */
ierr = set_cmb_entropy_gradient_update( E, rhs_b );CHKERRQ(ierr);
- /* must be here since must be after dS/dt computation
+ /* must be here since must be after dS/dt computation
only relevant for 2 phases. Perhaps need to tidy up similar
functionality, like output of rheological front, etc. */
if( P->n_phases == 2 ){
@@ -106,7 +110,7 @@ PetscErrorCode RHSFunction(TS ts,PetscReal t,Vec sol_in,Vec rhs,void *ptr)
ierr = VecCopy(rhs_b,subVecs[E->solutionSlots[SPIDER_SOLUTION_FIELD_DSDXI_B]]);CHKERRQ(ierr);
/* S0 */
- ierr = VecSetValue(subVecs[E->solutionSlots[SPIDER_SOLUTION_FIELD_S0]],0,arr_dSdt_s[0],INSERT_VALUES);CHKERRQ(ierr);
+ ierr = VecSetValue(subVecs[E->solutionSlots[SPIDER_SOLUTION_FIELD_S0]],0,dSdt_s0,INSERT_VALUES);CHKERRQ(ierr);
/* volatiles and reactions */
@@ -138,4 +142,4 @@ PetscErrorCode RHSFunction(TS ts,PetscReal t,Vec sol_in,Vec rhs,void *ptr)
ierr = VecDestroy(&rhs_b);CHKERRQ(ierr);
PetscFunctionReturn(0);
-}
+}
\ No newline at end of file
diff --git a/tests/tests.yml b/tests/tests.yml
index 91e5ee93..a9fea195 100644
--- a/tests/tests.yml
+++ b/tests/tests.yml
@@ -19,8 +19,8 @@ tests:
-
name: plot_test
commands:
- - HERE/../spider -options_file HERE/opts/blackbody50.opts -nstepsmacro 2
- - HERE/../py/plot_spider_lite.py
+ - HERE/../spider -options_file HERE/opts/blackbody50.opts -nstepsmacro 15
+ - HERE/../py/plot_spider_lite.py --times 0,100,200,300,400,500,600,700,800,900,1000,1100,1200,1300,1400,1500
type: exit_code
-
name: external_mesh_roundtrip
diff --git a/tools/get_petsc.sh b/tools/get_petsc.sh
new file mode 100755
index 00000000..fd9adffd
--- /dev/null
+++ b/tools/get_petsc.sh
@@ -0,0 +1,433 @@
+#!/usr/bin/env bash
+# =============================================================================
+# get_petsc.sh — Download, configure, and compile PETSc for SPIDER
+# =============================================================================
+#
+# Downloads PETSc 3.19.0 from OSF and builds it with sundials2 support.
+# SPIDER is a pure C code, so C++ and Fortran compilers are disabled.
+#
+# This script is intended to live inside the SPIDER repository:
+#
+# SPIDER/
+# ├── tools/
+# │ └── get_petsc.sh
+# ├── Makefile
+# └── ...
+#
+# Default install locations:
+# - Standalone clone: /SPIDER/petsc/
+# - Nested clone: /PROTEUS/petsc/
+#
+# An optional first argument may be supplied to choose a different PETSc path.
+# The argument is interpreted as the final PETSc directory itself:
+#
+# ./tools/get_petsc.sh
+# ./tools/get_petsc.sh /path/to/petsc
+#
+# Logs:
+# Full build logs are written to:
+# /logs/get_petsc-YYYYmmdd-HHMMSS.log
+#
+# Supported platforms:
+# - macOS 10.15 (Catalina) and later, Intel and Apple Silicon
+# - Linux (Ubuntu, Debian, Fedora/RHEL, HPC clusters)
+#
+# Prerequisites:
+# macOS: brew install gcc open-mpi
+# xcode-select --install
+# Ubuntu: sudo apt install build-essential libopenmpi-dev unzip curl
+# Fedora: sudo dnf install gcc openmpi openmpi-devel lapack lapack-devel \
+# lapack-static f2c f2c-libs unzip curl
+#
+# Environment after completion:
+# PETSC_DIR =
+# PETSC_ARCH = arch-{linux,darwin}-c-opt
+#
+# =============================================================================
+
+set -euo pipefail
+
+# -----------------------------------------------------------------------------
+# Console stream setup
+# -----------------------------------------------------------------------------
+if tty -s 2>/dev/null && [[ -w /dev/tty ]]; then
+ exec 3>/dev/tty
+ exec 4>/dev/tty
+else
+ exec 3>&1
+ exec 4>&2
+fi
+
+console() {
+ printf '%s\n' "$*" >&3
+}
+
+announce() {
+ printf '%s\n' "$*" >&3
+ printf '%s\n' "$*"
+}
+
+# -----------------------------------------------------------------------------
+# Portable path helpers
+# -----------------------------------------------------------------------------
+portable_realpath() {
+ if command -v realpath >/dev/null 2>&1; then
+ realpath "$1"
+ else
+ python3 -c "import os,sys; print(os.path.realpath(sys.argv[1]))" "$1"
+ fi
+}
+
+# -----------------------------------------------------------------------------
+# Repository layout helper
+# -----------------------------------------------------------------------------
+default_petsc_path_for_repo() {
+ local repo_root="$1"
+ local parent_root
+
+ parent_root="$(dirname "$repo_root")"
+
+ # If this checkout lives at PROTEUS/SPIDER, prefer PROTEUS/petsc so that
+ # PETSc is shared with the wider PROTEUS tree.
+ if [[ "$(basename "$repo_root")" == "SPIDER" ]] && [[ "$(basename "$parent_root")" == "PROTEUS" ]]; then
+ printf '%s/petsc\n' "$parent_root"
+ else
+ printf '%s/petsc\n' "$repo_root"
+ fi
+}
+
+# -----------------------------------------------------------------------------
+# Safety guard for destructive operations
+# -----------------------------------------------------------------------------
+safe_to_remove_dir() {
+ local target="$1"
+ local resolved
+
+ [[ -n "$target" ]] || return 1
+ resolved="$(portable_realpath "$target")"
+
+ case "$resolved" in
+ /|/home|/root|"${HOME}"|.)
+ return 1
+ ;;
+ esac
+
+ # This installer should only ever remove a PETSc directory
+ [[ "$(basename "$resolved")" == "petsc" ]] || return 1
+
+ return 0
+}
+
+# -----------------------------------------------------------------------------
+# Error handling: report which step failed on any non-zero exit
+# -----------------------------------------------------------------------------
+current_step="initialising"
+url="https://osf.io/download/p5vxq/"
+logfile=""
+
+on_error() {
+ local rc=$?
+
+ console ""
+ console "========================================"
+ console " ERROR: PETSc installation failed"
+ console ""
+ console " Step that failed: $current_step"
+ console " Command: $BASH_COMMAND"
+ console " Exit code: $rc"
+ if [[ -n "${logfile:-}" ]]; then
+ console " Log file: $logfile"
+ fi
+ console ""
+ console " Troubleshooting:"
+
+ case "$current_step" in
+ *"required tools"*)
+ console " - Install the missing prerequisite and re-run the script"
+ console " - On minimal HPC/login nodes, load any needed modules first"
+ ;;
+ *"Preparing PETSc directory"*)
+ console " - The target directory may have been rejected by the safety guard"
+ console " - Check that the install path is correct and ends in 'petsc'"
+ console " - Refused paths include '/', '$HOME', '.', and very short paths"
+ ;;
+ *"Download"*)
+ console " - Check your internet connection"
+ console " - Verify the OSF URL is accessible: $url"
+ console " - Try downloading manually: curl -fLsS \"$url\" -o petsc.zip"
+ ;;
+ *"Decompress"*)
+ console " - The downloaded archive may be corrupted"
+ console " - Delete the PETSc directory and re-run this script"
+ ;;
+ *"Configure"*)
+ console " - Check PETSc configure output in the log file"
+ console " - On macOS: ensure Xcode CLI tools are installed (xcode-select --install)"
+ console " - Verify MPI is installed (mpicc --version)"
+ ;;
+ *"Build"*)
+ console " - Check PETSc build output in the log file"
+ console " - Ensure your C compiler is working (mpicc --version)"
+ console " - On macOS: verify SDKROOT is set (xcrun --show-sdk-path)"
+ ;;
+ *"Test"*)
+ console " - PETSc built but tests failed"
+ console " - Check the test output in the log file"
+ console " - On macOS: check /etc/hosts for localhost entry"
+ ;;
+ *)
+ console " - Review the log file for the failing command"
+ ;;
+ esac
+
+ console "========================================"
+}
+trap on_error ERR
+
+# -----------------------------------------------------------------------------
+# 1. Detect platform and set PETSC_ARCH
+# -----------------------------------------------------------------------------
+current_step="Detecting platform"
+
+if [[ "$OSTYPE" == "linux"* ]]; then
+ export PETSC_ARCH=arch-linux-c-opt
+elif [[ "$OSTYPE" == "darwin"* ]]; then
+ export PETSC_ARCH=arch-darwin-c-opt
+else
+ echo "ERROR: Unsupported OS type '$OSTYPE'. Only Linux and macOS are supported." >&2
+ exit 1
+fi
+
+# -----------------------------------------------------------------------------
+# 2. Verify required tools are available
+# -----------------------------------------------------------------------------
+current_step="Verifying required tools"
+
+for cmd in curl unzip make; do
+ if ! command -v "$cmd" >/dev/null 2>&1; then
+ echo "ERROR: Required command '$cmd' not found in PATH." >&2
+ echo "Install it first and re-run this script." >&2
+ exit 1
+ fi
+done
+
+# realpath is optional, but python3 is required as the fallback
+if ! command -v realpath >/dev/null 2>&1 && ! command -v python3 >/dev/null 2>&1; then
+ echo "ERROR: Neither 'realpath' nor 'python3' was found in PATH." >&2
+ echo "Install one of them so the script can resolve paths correctly." >&2
+ exit 1
+fi
+
+# -----------------------------------------------------------------------------
+# 3. Determine SPIDER repo root and target PETSc directory
+# -----------------------------------------------------------------------------
+current_step="Setting up working directory"
+
+script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
+repo_root="$(dirname "$script_dir")"
+
+# Optional argument is the final PETSc directory itself.
+if [[ $# -ge 1 ]]; then
+ mkdir -p "$1"
+ workpath="$(portable_realpath "$1")"
+else
+ workpath="$(default_petsc_path_for_repo "$repo_root")"
+fi
+
+export PETSC_DIR="$workpath"
+
+# -----------------------------------------------------------------------------
+# 4. Set up logging
+# -----------------------------------------------------------------------------
+current_step="Setting up logging"
+
+log_dir="$(dirname "$workpath")/logs"
+mkdir -p "$log_dir"
+timestamp="$(date +%Y%m%d-%H%M%S)"
+logfile="$log_dir/get_petsc-$timestamp.log"
+
+exec >>"$logfile" 2>&1
+
+announce "Logging PETSc build to: $logfile"
+announce "PETSC_DIR = $PETSC_DIR"
+announce "PETSC_ARCH = $PETSC_ARCH"
+
+# -----------------------------------------------------------------------------
+# 5. Prepare PETSc directory
+# -----------------------------------------------------------------------------
+current_step="Preparing PETSc directory"
+
+if [[ -e "$workpath" ]]; then
+ if ! safe_to_remove_dir "$workpath"; then
+ echo "ERROR: Refusing to remove unsafe path: $workpath" >&2
+ exit 1
+ fi
+ rm -rf "$workpath"
+fi
+mkdir -p "$workpath"
+
+# -----------------------------------------------------------------------------
+# 6. Download PETSc 3.19.0 from OSF
+# -----------------------------------------------------------------------------
+current_step="Downloading PETSc archive from OSF"
+
+zipfile="$workpath/petsc.zip"
+announce ""
+announce "Downloading PETSc archive from OSF..."
+announce " $url -> $zipfile"
+curl -fLsS "$url" -o "$zipfile"
+
+current_step="Decompressing PETSc archive"
+announce "Decompressing..."
+unzip -qq "$zipfile" -d "$workpath"
+rm -f "$zipfile"
+
+# -----------------------------------------------------------------------------
+# 7. Determine platform-specific configure flags
+# -----------------------------------------------------------------------------
+current_step="Determining platform-specific flags"
+
+mpi_flag="--download-mpich"
+blas_flag="--download-f2cblaslapack"
+ldflags=""
+cflags=""
+
+# ---- Linux special cases ----------------------------------------------------
+if [[ "$OSTYPE" == "linux"* ]]; then
+ host="$(hostname -f 2>/dev/null || hostname)"
+
+ if [[ "$host" == *"snellius"* ]]; then
+ announce " Detected Snellius cluster — using system MPI"
+ mpi_flag=""
+ elif [[ "$host" == *"hpc.rug.nl" ]]; then
+ announce " Detected Habrok cluster"
+ elif [[ -f "/etc/fedora-release" || -f "/etc/redhat-release" ]]; then
+ announce " Detected Fedora/RHEL"
+ if command -v mpicc >/dev/null 2>&1; then
+ announce " Found system MPI ($(command -v mpicc)) — skipping mpich download"
+ mpi_flag=""
+ else
+ announce " mpicc not in PATH — will download MPICH"
+ fi
+ blas_flag=""
+ cflags="-fPIC -Wno-error=format-security -Wno-lto-type-mismatch -Wno-stringop-overflow"
+ elif command -v mpicc >/dev/null 2>&1; then
+ announce " Found system MPI ($(command -v mpicc)) — skipping mpich download"
+ mpi_flag=""
+ fi
+fi
+
+# ---- macOS ------------------------------------------------------------------
+if [[ "$OSTYPE" == "darwin"* ]]; then
+ if ! command -v xcrun >/dev/null 2>&1; then
+ echo "ERROR: xcrun not found. Install Xcode Command Line Tools:" >&2
+ echo " xcode-select --install" >&2
+ exit 1
+ fi
+
+ export SDKROOT
+ SDKROOT="$(xcrun --show-sdk-path)"
+ announce " SDKROOT = $SDKROOT"
+
+ if command -v mpicc >/dev/null 2>&1; then
+ announce " Found system MPI ($(command -v mpicc)) — skipping mpich download"
+ mpi_flag=""
+ else
+ announce "WARNING: mpicc not found. Install MPI via Homebrew:"
+ announce " brew install open-mpi"
+ announce "Falling back to --download-mpich"
+ fi
+
+ # macOS provides Accelerate framework with BLAS/LAPACK
+ blas_flag=""
+
+ if [[ "$(uname -m)" == "arm64" ]]; then
+ default_brew_prefix="/opt/homebrew"
+ else
+ default_brew_prefix="/usr/local"
+ fi
+ brew_prefix="$(brew --prefix 2>/dev/null || echo "$default_brew_prefix")"
+ ldflags="-L${brew_prefix}/lib -Wl,-w"
+fi
+
+if [[ -z "$mpi_flag" ]] && ! command -v mpirun >/dev/null 2>&1; then
+ echo "ERROR: MPI not found and --download-mpich was disabled." >&2
+ echo "Install MPI first (e.g. 'brew install open-mpi' or 'apt install libopenmpi-dev')." >&2
+ exit 1
+fi
+
+# -----------------------------------------------------------------------------
+# 8. Configure PETSc
+# -----------------------------------------------------------------------------
+current_step="Configuring PETSc (./configure)"
+
+announce ""
+announce "Configuring PETSc..."
+announce " MPI: ${mpi_flag:-system}"
+announce " BLAS: ${blas_flag:-system}"
+announce " CFLAGS: ${cflags:-}"
+announce " LDFLAGS: ${ldflags:-}"
+
+olddir="$(pwd)"
+cd "$workpath"
+
+configure_args=(
+ --with-debugging=0
+ --with-fc=0
+ --with-cxx=0
+ --download-sundials2
+ "--COPTFLAGS=-g -O3"
+)
+
+if [[ -n "$mpi_flag" ]]; then
+ configure_args+=("$mpi_flag")
+fi
+if [[ -n "$blas_flag" ]]; then
+ configure_args+=("$blas_flag")
+fi
+if [[ -n "$cflags" ]]; then
+ configure_args+=("CFLAGS=$cflags")
+fi
+if [[ -n "$ldflags" ]]; then
+ configure_args+=("LDFLAGS=$ldflags")
+fi
+
+./configure "${configure_args[@]}"
+
+# -----------------------------------------------------------------------------
+# 9. Build PETSc
+# -----------------------------------------------------------------------------
+current_step="Building PETSc (make all)"
+
+ncpu=4
+announce ""
+announce "Building PETSc with $ncpu CPUs..."
+make PETSC_DIR="$PETSC_DIR" PETSC_ARCH="$PETSC_ARCH" -j "$ncpu" all
+
+# -----------------------------------------------------------------------------
+# 10. Run PETSc self-tests
+# -----------------------------------------------------------------------------
+current_step="Testing PETSc (make check)"
+
+announce ""
+announce "Testing PETSc..."
+make PETSC_DIR="$PETSC_DIR" PETSC_ARCH="$PETSC_ARCH" check
+
+# -----------------------------------------------------------------------------
+# 11. Done
+# -----------------------------------------------------------------------------
+cd "$olddir"
+
+announce ""
+announce "========================================"
+announce " PETSc installation complete."
+announce ""
+announce " PETSC_DIR = $PETSC_DIR"
+announce " PETSC_ARCH = $PETSC_ARCH"
+announce " Log file: $logfile"
+announce ""
+announce " Add these to your shell config if you"
+announce " need to rebuild SPIDER manually:"
+announce " export PETSC_DIR=$PETSC_DIR"
+announce " export PETSC_ARCH=$PETSC_ARCH"
+announce "========================================"
\ No newline at end of file
diff --git a/tools/get_spider.sh b/tools/get_spider.sh
new file mode 100755
index 00000000..c4a0f5f6
--- /dev/null
+++ b/tools/get_spider.sh
@@ -0,0 +1,358 @@
+#!/usr/bin/env bash
+# =============================================================================
+# get_spider.sh — Build SPIDER from an already-cloned checkout
+# =============================================================================
+#
+# Builds the SPIDER interior evolution model against a local PETSc
+# installation. PETSc is installed automatically first if it is missing.
+#
+# SPIDER is a pure C code that uses PETSc for numerics and sundials2 for
+# ODE integration. The Makefile includes PETSc's build rules, so PETSC_DIR
+# and PETSC_ARCH must be set correctly.
+#
+# Supported platforms:
+# - macOS 10.15 (Catalina) and later, Intel and Apple Silicon
+# - Linux (Ubuntu, Debian, Fedora/RHEL, HPC clusters)
+#
+# Supported repository layouts:
+# - Standalone clone: /SPIDER/tools/get_spider.sh
+# -> PETSc defaults to /SPIDER/petsc/
+# - Nested clone: /PROTEUS/SPIDER/tools/get_spider.sh
+# -> PETSc defaults to /PROTEUS/petsc/
+#
+# Prerequisites:
+# - A cloned SPIDER repository (this script builds the current checkout)
+# - C compiler accessible via MPI wrapper (mpicc), or PETSc's MPICH download
+# - make
+#
+# Usage:
+# ./tools/get_spider.sh # build the current checkout in place
+# ./tools/get_spider.sh /path/to/SPIDER # build a different cloned checkout
+#
+# Logs:
+# Full build logs are written to:
+# /logs/get_spider-YYYYmmdd-HHMMSS.log
+#
+# The script is suitable for standalone SPIDER installs, and still behaves
+# nicely inside a PROTEUS/SPIDER checkout by installing PETSc in PROTEUS/petsc.
+#
+# =============================================================================
+
+set -euo pipefail
+
+# -----------------------------------------------------------------------------
+# Console stream setup
+# -----------------------------------------------------------------------------
+if tty -s 2>/dev/null && [[ -w /dev/tty ]]; then
+ exec 3>/dev/tty
+ exec 4>/dev/tty
+else
+ exec 3>&1
+ exec 4>&2
+fi
+
+console() {
+ printf '%s\n' "$*" >&3
+}
+
+announce() {
+ printf '%s\n' "$*" >&3
+ printf '%s\n' "$*"
+}
+
+# -----------------------------------------------------------------------------
+# Portable path helpers
+# -----------------------------------------------------------------------------
+portable_realpath() {
+ if command -v realpath >/dev/null 2>&1; then
+ realpath "$1"
+ else
+ python3 -c "import os,sys; print(os.path.realpath(sys.argv[1]))" "$1"
+ fi
+}
+
+portable_abspath() {
+ if command -v realpath >/dev/null 2>&1; then
+ realpath -m "$1" 2>/dev/null || python3 -c "import os,sys; print(os.path.abspath(sys.argv[1]))" "$1"
+ else
+ python3 -c "import os,sys; print(os.path.abspath(sys.argv[1]))" "$1"
+ fi
+}
+
+# -----------------------------------------------------------------------------
+# Repository layout helpers
+# -----------------------------------------------------------------------------
+default_petsc_path_for_repo() {
+ local repo_root="$1"
+ local parent_root
+
+ parent_root="$(dirname "$repo_root")"
+
+ # If this checkout lives at PROTEUS/SPIDER, prefer PROTEUS/petsc so that
+ # PETSc is shared with the wider PROTEUS tree.
+ if [[ "$(basename "$repo_root")" == "SPIDER" ]] && [[ "$(basename "$parent_root")" == "PROTEUS" ]]; then
+ printf '%s/petsc\n' "$parent_root"
+ else
+ printf '%s/petsc\n' "$repo_root"
+ fi
+}
+
+petsc_built_ok() {
+ local petsc_dir="$1"
+ local petsc_arch="$2"
+ local petsc_lib_dir petsc_conf_dir f
+
+ [[ -d "$petsc_dir" ]] || return 1
+
+ petsc_lib_dir="$petsc_dir/$petsc_arch/lib"
+ petsc_conf_dir="$petsc_dir/lib/petsc/conf"
+
+ for f in "$petsc_lib_dir"/libpetsc.*; do
+ [[ -f "$f" ]] || continue
+ [[ -f "$petsc_conf_dir/variables" ]] || return 1
+ [[ -f "$petsc_conf_dir/rules" ]] || return 1
+ return 0
+ done
+
+ return 1
+}
+
+# -----------------------------------------------------------------------------
+# Error handling: report which step failed on any non-zero exit
+# -----------------------------------------------------------------------------
+current_step="initialising"
+logfile=""
+
+on_error() {
+ local rc=$?
+
+ console ""
+ console "========================================"
+ console " ERROR: SPIDER installation failed"
+ console ""
+ console " Step that failed: $current_step"
+ console " Command: $BASH_COMMAND"
+ console " Exit code: $rc"
+ if [[ -n "${logfile:-}" ]]; then
+ console " Log file: $logfile"
+ fi
+ console ""
+ console " Troubleshooting:"
+
+ case "$current_step" in
+ *"required tools"*)
+ console " - Install the missing prerequisite and re-run the script"
+ console " - On minimal HPC/login nodes, load any needed modules first"
+ ;;
+ *"PETSc"*)
+ console " - Check the PETSc installer output above for errors"
+ console " - Re-run ./tools/get_petsc.sh manually if needed"
+ ;;
+ *"Building"*)
+ console " - Check the compiler output in the log file"
+ console " - Verify mpicc is working: mpicc --version"
+ console " - Verify PETSc is intact: ls \$PETSC_DIR/\$PETSC_ARCH/lib/libpetsc.*"
+ console " - On macOS: ensure SDKROOT is set (xcrun --show-sdk-path)"
+ ;;
+ *"Verif"*)
+ console " - The build completed without make errors but no binary was produced"
+ console " - This usually indicates a linker failure that was suppressed"
+ console " - Try rebuilding with verbose output: make V=1"
+ ;;
+ *)
+ console " - Review the log file for the failing command"
+ ;;
+ esac
+
+ console ""
+ console " PETSc environment used:"
+ console " PETSC_DIR = ${PETSC_DIR:-}"
+ console " PETSC_ARCH = ${PETSC_ARCH:-}"
+ console "========================================"
+}
+trap on_error ERR
+
+# -----------------------------------------------------------------------------
+# 1. Detect platform and set PETSC_ARCH
+# -----------------------------------------------------------------------------
+current_step="Detecting platform"
+
+if [[ "$OSTYPE" == linux* ]]; then
+ PETSC_ARCH=arch-linux-c-opt
+elif [[ "$OSTYPE" == darwin* ]]; then
+ PETSC_ARCH=arch-darwin-c-opt
+else
+ echo "ERROR: Unsupported OS type '$OSTYPE'. Only Linux and macOS are supported." >&2
+ exit 1
+fi
+
+# -----------------------------------------------------------------------------
+# 2. Verify required tools are available
+# -----------------------------------------------------------------------------
+current_step="Verifying required tools"
+
+for cmd in make; do
+ if ! command -v "$cmd" >/dev/null 2>&1; then
+ echo "ERROR: Required command '$cmd' not found in PATH." >&2
+ exit 1
+ fi
+done
+
+# portable_abspath / portable_realpath need either realpath or python3
+if ! command -v realpath >/dev/null 2>&1 && ! command -v python3 >/dev/null 2>&1; then
+ echo "ERROR: Neither 'realpath' nor 'python3' was found in PATH." >&2
+ echo "Install one of them so the script can resolve paths correctly." >&2
+ exit 1
+fi
+
+# -----------------------------------------------------------------------------
+# 3. Locate SPIDER checkout to build
+# -----------------------------------------------------------------------------
+current_step="Locating SPIDER checkout"
+
+script_dir="$(cd "$(dirname "$0")" && pwd)"
+default_repo_root="$(dirname "$script_dir")"
+
+if [[ -n "${1:-}" ]]; then
+ workpath="$(portable_abspath "$1")"
+else
+ workpath="$default_repo_root"
+fi
+
+if [[ ! -d "$workpath" ]]; then
+ echo "ERROR: SPIDER directory not found at $workpath." >&2
+ exit 1
+fi
+
+if [[ ! -f "$workpath/Makefile" ]]; then
+ echo "ERROR: No Makefile found in $workpath." >&2
+ echo "Expected an already-cloned SPIDER checkout." >&2
+ exit 1
+fi
+
+if [[ ! -x "$workpath/tools/get_petsc.sh" ]]; then
+ echo "ERROR: Could not find executable PETSc installer at $workpath/tools/get_petsc.sh." >&2
+ echo "Ensure this script lives inside the SPIDER checkout." >&2
+ exit 1
+fi
+
+workpath="$(portable_realpath "$workpath")"
+
+# -----------------------------------------------------------------------------
+# 4. Set up logging
+# -----------------------------------------------------------------------------
+current_step="Setting up logging"
+
+log_dir="$workpath/logs"
+mkdir -p "$log_dir"
+timestamp="$(date +%Y%m%d-%H%M%S)"
+logfile="$log_dir/get_spider-$timestamp.log"
+
+exec >>"$logfile" 2>&1
+
+announce "Logging SPIDER build to: $logfile"
+
+# -----------------------------------------------------------------------------
+# 5. Locate or bootstrap PETSc installation
+# -----------------------------------------------------------------------------
+current_step="Validating PETSc installation"
+
+if [[ -n "${PETSC_DIR:-}" ]]; then
+ petsc_path="$PETSC_DIR"
+else
+ petsc_path="$(default_petsc_path_for_repo "$workpath")"
+fi
+
+if ! petsc_built_ok "$petsc_path" "$PETSC_ARCH"; then
+ current_step="Installing PETSc via tools/get_petsc.sh"
+ announce "PETSc not found (or incomplete) at $petsc_path"
+ announce "Bootstrapping PETSc first..."
+ announce "The PETSc installer will create its own log file."
+ "$workpath/tools/get_petsc.sh" "$petsc_path"
+ current_step="Validating PETSc installation"
+fi
+
+if ! petsc_built_ok "$petsc_path" "$PETSC_ARCH"; then
+ echo "ERROR: PETSc library/configuration files not found after installation." >&2
+ echo "Checked: $petsc_path" >&2
+ exit 1
+fi
+
+export PETSC_DIR="$(portable_realpath "$petsc_path")"
+export PETSC_ARCH
+
+announce "PETSC_DIR = $PETSC_DIR"
+announce "PETSC_ARCH = $PETSC_ARCH"
+
+# -----------------------------------------------------------------------------
+# 6. macOS-specific environment setup
+# -----------------------------------------------------------------------------
+if [[ "$OSTYPE" == darwin* ]]; then
+ if command -v xcrun >/dev/null 2>&1; then
+ export SDKROOT
+ SDKROOT="$(xcrun --show-sdk-path)"
+ announce "SDKROOT = $SDKROOT"
+ fi
+fi
+
+# -----------------------------------------------------------------------------
+# 7. Verify build tools are available
+# -----------------------------------------------------------------------------
+current_step="Verifying build tools"
+
+if ! command -v mpicc >/dev/null 2>&1; then
+ announce "WARNING: mpicc not found in PATH."
+ announce " This is fine if PETSc was built with downloaded MPICH and the"
+ announce " wrapper is available through PETSc's build rules during make."
+fi
+
+# -----------------------------------------------------------------------------
+# 8. Build SPIDER
+# -----------------------------------------------------------------------------
+current_step="Building SPIDER (make)"
+
+if command -v nproc >/dev/null 2>&1; then
+ njobs="$(nproc)"
+elif command -v sysctl >/dev/null 2>&1; then
+ njobs="$(sysctl -n hw.ncpu)"
+else
+ njobs=2
+fi
+
+announce ""
+announce "Building SPIDER in $workpath ($njobs parallel jobs)..."
+olddir="$(pwd)"
+cd "$workpath"
+
+make -j "$njobs"
+
+# -----------------------------------------------------------------------------
+# 9. Verify the build produced the SPIDER binary
+# -----------------------------------------------------------------------------
+current_step="Verifying SPIDER binary"
+
+if [[ ! -x "$workpath/spider" ]]; then
+ echo "ERROR: SPIDER binary not found after build." >&2
+ echo "Check the build output in the log file." >&2
+ cd "$olddir"
+ exit 1
+fi
+
+spider_version="$("$workpath/spider" --help 2>&1 | head -1 || true)"
+announce ""
+announce "Build successful: $spider_version"
+
+# -----------------------------------------------------------------------------
+# 10. Done
+# -----------------------------------------------------------------------------
+cd "$olddir"
+
+announce ""
+announce "========================================"
+announce " SPIDER installation complete."
+announce ""
+announce " Binary: $(portable_realpath "$workpath/spider")"
+announce " PETSC_DIR = $PETSC_DIR"
+announce " PETSC_ARCH = $PETSC_ARCH"
+announce " Log file: $logfile"
+announce "========================================"
\ No newline at end of file