Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# This workflow will install Python dependencies, run tests and lint with a variety of Python versions
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions

name: tests

on:
push:
branches: [ main ]
paths:
- '**.py'
- '.github/workflows/tests.yml'
pull_request:
branches: [ main ]
paths:
- '**.py'
- '.github/workflows/tests.yml'


jobs:
build:

runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dependencies
run: python3 -m pip install '.[test]'
- name: Lint with flake8
run: |
python3 -m pip install flake8
# stop the build if there are Python syntax errors or undefined names
flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
# exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide
flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics
- name: Test with pytest
run: |
python3 -m pytest
3 changes: 1 addition & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,7 @@ __pycache__/

.venv

Output/*
evo/Output/*
outputs/*

# sphinx docs
docs/_build/
Expand Down
12 changes: 6 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ EVo can be set up using either melt volatile contents, or for a set amount of at

### Prerequisites

This programme requires Python 3 to run.
This programme requires Python 3 to run.

If you use python virtual environments, or Anaconda, requirements files (requirements.txt and environment.yml for virtualenv and conda, respectively) can be found in the Data file.

Expand All @@ -29,19 +29,19 @@ To install locally, EVo must be downloaded from GitHub using
into the project directory where you wish to use EVo. EVo must then be locally pip-installed:
```
cd EVO
python -m pip install -e evo/
python -m pip install -e .
```

From this point, EVo can either be imported into your python scripts as a regular module using
`install evo`
`import evo`

and run using
`evo.main('chem_file', 'env_file', 'output_options_file')`
`evo.main(<chem_file>, <env_file>, <output_options_file>, folder=<output folder name>)`

Or EVo can be run directly from the terminal from inside the `evo` directory:
```
cd EVO/evo
python dgs.py input/chem.yaml input/env.yaml --output input/output.yaml
python dgs.py input/chem.yaml input/env.yaml --output-options input/output.yaml
```

The model should run and produce an output file Outputs/dgs_output_*.csv, and a set of graphs in the Output folder, if a decompression run has been selected.
Expand All @@ -53,7 +53,7 @@ The different run types, model parameters and input values are all set in the en
There are multiple run types which can be selected within EVo. At the highest level, a run can either be (1). single pressure, where equilibration between the gas and the melt only occurs at 1 pressure step set using P_START, or (2) a decompression run, where calculations start at P_START and run through pressure steps (the max and min size of which can be set using DP_MAX and DP_MIN respectively) until P_STOP is reached. These two high-level un options can be toggled between using SINGLE_STEP, where True sets up EVo to do a single pressure run, and False asks for decompression.

Within these two high-level run types, 3 options for selecting starting conditions are available:
* Standard: pick a pressure (P_START), a starting gas weight fraction (WgT) and some combination of either current melt volatile contents, or gas fugacities. EVo will calculate the missing variables and either stop at that point for a single pressure run, or continue on in decompression mode. This is the default option, and will be used if both FIND_SATURATION and ATOMIC_MASS_SET are False.
* Standard: pick a pressure (P_START), a starting gas weight fraction (WgT) and some combination of either current melt volatile contents, or gas fugacities. EVo will calculate the missing variables and either stop at that point for a single pressure run, or continue on in decompression mode. This is the default option, and will be used if both FIND_SATURATION and ATOMIC_MASS_SET are False.
* Volatile saturation: Chosen by switching FIND_SATURATION to True, given only the melt volatile contents and magma fO2, EVo will calculate the volatile saturation pressure and start a run from there. Any values given in P_START and WgT will be ignored; WgT will be set to 1e-8 at the volatile saturation point.
* Atomic set: Chosen by switching ATOMIC_MASS_SET to True (and FIND_SATURATION to False). Given the melt fO2 and the atomic weight fractions of each of the other volatile species (H, +/- C, S & N, set using ATOMIC_H etc.), Evo will calculate both the appropriate distribution of each element across the different species considered, and the volatile saturation point of that composition. Particularly useful for studies where fO2 is varied but the amount of other elements should be held constant. Again, P_START and WgT will be ignored, WgT will be set to 1e-8 at the volatile saturation point.

Expand Down
20 changes: 14 additions & 6 deletions evo/dgs.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
# python main [ ensure these are on your system ]
import argparse
import time
from pathlib import Path

import numpy as np

Expand All @@ -74,7 +75,7 @@
# ------------------------------------------------------------------------


def main(f_chem, f_env, f_out):
def main(f_chem, f_env, f_out, folder="outputs"):
"""Main function for EVo.

Call to run the model.
Expand All @@ -87,6 +88,8 @@ def main(f_chem, f_env, f_out):
Path to the environment input file
f_out : str or NoneType
Path to the file describing the required outputs, None if not used
folder : str
Path to the folder to write the results to
"""
start = time.time()

Expand All @@ -97,6 +100,7 @@ def main(f_chem, f_env, f_out):

# Instantiate the run, thermosystem, melt and output objects
run, sys, melt, out = readin(f_chem, f_env, f_out)
run.results_folder = Path(folder)

print("Set parameters:")
run.print_conditions()
Expand Down Expand Up @@ -228,19 +232,23 @@ def main(f_chem, f_env, f_out):
)

my_parser.add_argument(
"--output",
"--output-options",
help="use selected output options from output.yaml file",
)

my_parser.add_argument(
"-o", "--output", help="the folder location to write the results to"
)

# Parse in files
args = my_parser.parse_args()

f_chem = args.chem # set chemical compositions file
f_env = args.env # set environment file

if args.output:
f_out = args.output # set output file as an optional input
main(f_chem, f_env, f_out)
if args.output_options:
f_out = args.output_options # set output file as an optional input
main(f_chem, f_env, f_out, folder=args.output)
else:
f_out = None
main(f_chem, f_env, f_out)
main(f_chem, f_env, f_out, folder=args.output)
2 changes: 2 additions & 0 deletions evo/dgs_classes.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ def __init__(self):
self.GRAPHITE_SATURATED = False
self.GRAPHITE_START = 0.0 # initial melt graphite content

self.results_folder = None

def param_set(self, params):
"""
Sets class up with parameters from the environment file.
Expand Down
32 changes: 14 additions & 18 deletions evo/writeout.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,6 @@
contains options to produce a graph of the results.
"""

import glob
import os
from pathlib import Path

import matplotlib.pyplot as plt
import numpy as np
import pandas as pd
Expand Down Expand Up @@ -168,9 +164,9 @@ def writeout_file(sys, gas, melt, P, crashed=False):

df = pd.DataFrame(data)

filepath = Path(__file__).parent / "Output"
if not os.path.exists(filepath):
os.makedirs(filepath)
filepath = sys.run.results_folder
if not filepath.exists():
filepath.mkdir()

if not crashed:
file_name = (
Expand All @@ -183,7 +179,7 @@ def writeout_file(sys, gas, melt, P, crashed=False):
f"_{sys.run.RUN_TYPE}_{sys.T:.0f}K.csv"
)

output_path = os.path.join(filepath, file_name)
output_path = filepath / file_name

if sys.run.FIND_SATURATION is True or sys.run.ATOMIC_MASS_SET is True:
df_sat = pd.DataFrame(
Expand Down Expand Up @@ -227,14 +223,14 @@ def writeout_figs(sys, melt, gas, out, P):
List of the pressures each step was calculated at (bar)
"""

filepath = Path(__file__).parent / "Output"

filelist = glob.glob(str(filepath / "*.png"))
filepath = sys.run.results_folder
# filelist = glob.glob(str(filepath / "*.png"))
filelist = list(filepath.glob("*.png"))
# Removes previous files so if output specification is changed
# there is no confusion as to up to date files.

for file in filelist:
os.remove(file)
file.unlink()
if (
out is not None
): # If an output file listing requested figures has been included:
Expand Down Expand Up @@ -272,7 +268,7 @@ def plot_fo2FMQ(melt, gas, P, path):
plt.xscale("log")
plt.xlabel("Pressure (bars)")
plt.ylabel(r"$\Delta$ FMQ")
plt.savefig("Output/FMQ.png")
plt.savefig(path / "FMQ.png")
plt.close()

fo2 = []
Expand All @@ -283,7 +279,7 @@ def plot_fo2FMQ(melt, gas, P, path):
# plt.xscale('log')
plt.xlabel("Pressure (bars)")
plt.ylabel("log(10) fO2")
plt.savefig(os.path.join(path, "fO2.png"))
plt.savefig(path / "fO2.png")
plt.close()


Expand Down Expand Up @@ -326,7 +322,7 @@ def plot_gasspecies_mol(gas, P, path):
plt.gca().invert_yaxis()
plt.xlabel(f"Speciation in a {gas.sys.run.GAS_SYS} gas (mol frac)")
plt.ylabel("Pressure (bar)")
plt.savefig(os.path.join(path, "speciation(mol).png"))
plt.savefig(path / "speciation(mol).png")
plt.close()


Expand Down Expand Up @@ -371,7 +367,7 @@ def plot_gasspecies_wt(gas, P, path):
plt.gca().invert_yaxis()
plt.xlabel(f"Gas phase speciation of a {gas.sys.run.GAS_SYS} system (wt %)")
plt.ylabel("Pressure (bar)")
plt.savefig(os.path.join(path, "speciation(wt).png"))
plt.savefig(path / "speciation(wt).png")
plt.close()


Expand Down Expand Up @@ -406,7 +402,7 @@ def plot_meltspecies(melt, P, path):
plt.gca().invert_yaxis()
plt.xlabel("Melt volatile content (wt%)")
plt.ylabel("Pressure (bar)")
plt.savefig(os.path.join(path, "meltspecies.png"))
plt.savefig(path / "meltspecies.png")
plt.close()


Expand All @@ -423,4 +419,4 @@ def plot_gasfraction(sys, P, path):
ax1.set_ylabel("Pressure (bar)")
ax2.plot(cnvt.frac2perc(sys.GvF), P)
ax2.set_xlabel("Exsolved gas volume %")
plt.savefig(os.path.join(path, "exsolved_gas.png"))
plt.savefig(path / "exsolved_gas.png")
3 changes: 2 additions & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ readme = "README.md"
license = {file = "LICENSE"}
classifiers = ["License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)",
"Programming Language :: Python :: 3"]
requires-python = ">=3.7"
requires-python = ">=3.9"

dependencies = ["numpy>=1.21.0",
"scipy>=1.7.0",
Expand All @@ -25,6 +25,7 @@ dependencies = ["numpy>=1.21.0",

[project.optional-dependencies]
dev = ["ruff", "pre-commit"]
test = ["pytest"]

[project.urls]
Home = "https://github.com/pipliggins/EVo"
Expand Down
7 changes: 5 additions & 2 deletions tests/integration/test_coh.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
import pandas as pd


def test_coh_default():
def test_coh_default(tmp_path):
df = evo.main(
"evo/input/chem.yaml", "tests/integration/input_files/env_coh.yaml", None
"evo/input/chem.yaml",
"tests/integration/input_files/env_coh.yaml",
None,
folder=tmp_path,
)

assert isinstance(df, pd.DataFrame)
7 changes: 5 additions & 2 deletions tests/integration/test_cohs.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
import pandas as pd


def test_cohs_default():
def test_cohs_default(tmp_path):
df = evo.main(
"evo/input/chem.yaml", "tests/integration/input_files/env_cohs.yaml", None
"evo/input/chem.yaml",
"tests/integration/input_files/env_cohs.yaml",
None,
folder=tmp_path,
)

assert isinstance(df, pd.DataFrame)
7 changes: 5 additions & 2 deletions tests/integration/test_cohsn.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
import pandas as pd


def test_cohsn_default():
def test_cohsn_default(tmp_path):
df = evo.main(
"evo/input/chem.yaml", "tests/integration/input_files/env_cohsn.yaml", None
"evo/input/chem.yaml",
"tests/integration/input_files/env_cohsn.yaml",
None,
folder=tmp_path,
)

assert isinstance(df, pd.DataFrame)
7 changes: 5 additions & 2 deletions tests/integration/test_oh.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
import pandas as pd


def test_oh_default():
def test_oh_default(tmp_path):
df = evo.main(
"evo/input/chem.yaml", "tests/integration/input_files/env_oh.yaml", None
"evo/input/chem.yaml",
"tests/integration/input_files/env_oh.yaml",
None,
folder=tmp_path,
)

assert isinstance(df, pd.DataFrame)
7 changes: 5 additions & 2 deletions tests/integration/test_soh.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@
import pandas as pd


def test_soh_default():
def test_soh_default(tmp_path):
df = evo.main(
"evo/input/chem.yaml", "tests/integration/input_files/env_soh.yaml", None
"evo/input/chem.yaml",
"tests/integration/input_files/env_soh.yaml",
None,
folder=tmp_path,
)

assert isinstance(df, pd.DataFrame)