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
29 changes: 29 additions & 0 deletions .github/workflows/docs.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Documentation
on:
push:
branches:
- master
- main
permissions:
contents: write
jobs:
deploy:
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/setup-python@v5
with:
python-version: "3.x"
- run: echo "cache_id=$(date --utc '+%V')" >> $GITHUB_ENV
- uses: actions/cache@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
- run: mkdocs gh-deploy --force --clean
2 changes: 1 addition & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ mkdocs serve
You can find the documentation source in the [docs](https://github.com/FormingWorlds/MORS/tree/main/docs) directory.
If you are adding new pages, make sure to update the listing in the [`mkdocs.yml`](https://github.com/FormingWorlds/MORS/blob/main/mkdocs.yml) under the `nav` entry.

The documentation is hosted on [readthedocs](https://fwl-mors.readthedocs.io).
The documentation is hosted on [PROTEUS Framework](https://proteus-framework.org/MORS/).
### Running tests

MORS uses [pytest](https://docs.pytest.org/en/latest/) to run the tests. You can run the tests for yourself using:
Expand Down
526 changes: 30 additions & 496 deletions README.md

Large diffs are not rendered by default.

526 changes: 526 additions & 0 deletions README_old.md

Large diffs are not rendered by default.

129 changes: 129 additions & 0 deletions docs/How-to/activity_quantities.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
## Rotation and activity quantities (How-to)

### Goal
Compute high-energy emission quantities (X-ray, EUV, Ly-α) from stellar **mass**, **age**, and **rotation**, optionally add variability/scatter, and (if needed) retrieve a larger set of model diagnostics via `ExtendedQuantities`.

### Prerequisites
Install MORS and download the required stellar evolution data:

```bash
pip install fwl-mors
mors download all
```

> **Units:** `Mstar` in **M☉**, `Age` in **Myr**, `Prot` in **days**, `Omega` in **Ω☉**. Luminosities are in **erg s⁻¹** and surface fluxes in **erg s⁻¹ cm⁻²**.

---

### Step 1: Get the full XUV dictionary (`Lxuv`)

Use `Omega` (or `OmegaEnv`) **or** `Prot` to specify the surface rotation.

**Using Ω (Ω☉):**
```python
import mors
xuv = mors.Lxuv(Mstar=1.0, Age=5000.0, Omega=10.0)
```

**Using rotation period (days):**
```python
import mors
xuv = mors.Lxuv(Mstar=1.0, Age=5000.0, Prot=1.0)
```

The returned dictionary includes luminosities, surface fluxes, and bolometric-normalized values, e.g.:
- Luminosities: `Lxuv`, `Lx`, `Leuv1`, `Leuv2`, `Leuv`, `Lly` (erg s⁻¹)
- Fluxes: `Fxuv`, `Fx`, `Feuv1`, ... (erg s⁻¹ cm⁻²)
- Normalized: `Rxuv`, `Rx`, `Reuv1`, ... (dimensionless)

To see what keys are present:
```python
print(sorted(xuv.keys()))
```

---

### Step 2: Retrieve a single quantity directly (`Lx`, `Leuv`, `Lly`)

```python
import mors

Lx = mors.Lx(Mstar=1.0, Age=5000.0, Omega=10.0)
Leu = mors.Leuv(Mstar=1.0, Age=5000.0, Omega=10.0)
Lly = mors.Lly(Mstar=1.0, Age=5000.0, Omega=10.0)
```

#### Choose an EUV sub-band with `band`
`Leuv(..., band=...)` supports:
- `band=0` → 10–92 nm (total EUV)
- `band=1` → 10–36 nm (EUV1)
- `band=2` → 36–92 nm (EUV2)

```python
import mors
Leuv_total = mors.Leuv(Mstar=1.0, Age=5000.0, Omega=10.0, band=0)
Leuv1 = mors.Leuv(Mstar=1.0, Age=5000.0, Omega=10.0, band=1)
Leuv2 = mors.Leuv(Mstar=1.0, Age=5000.0, Omega=10.0, band=2)
```

---

### Step 3: Add variability/scatter (optional)

MORS provides random scatter terms intended to represent variability as a log-normal scatter.

#### X-ray scatter (`XrayScatter`)
You pass the mean value (e.g., `Lx`) and get a delta to add:
```python
import mors

Lx = mors.Lx(Mstar=1.0, Age=5000.0, Omega=10.0)
dLx = mors.XrayScatter(Lx)

Lx_scattered = Lx + dLx
```

You can also pass `Fx` or `Rx` and receive `dFx` / `dRx`.

#### Full XUV scatter (`XUVScatter`)
`XUVScatter` takes the dictionary from `Lxuv` and returns a dictionary of deltas with the same keys:
```python
import mors

xuv = mors.Lxuv(Mstar=1.0, Age=5000.0, Omega=10.0)
dxuv = mors.XUVScatter(xuv)

# Example: apply deltas to get one scattered realization
xuv_scattered = {k: xuv[k] + dxuv[k] for k in xuv}
```

**Controlling the scatter width:** the X-ray scatter width is controlled by `sigmaXray` in the parameters file. You can override it by passing a custom `params` dictionary (see the parameter how-to):

```python
import mors

params = mors.NewParams(sigmaXray=0.3) # example value
Lx = mors.Lx(Mstar=1.0, Age=5000.0, Omega=10.0, params=params)
dLx = mors.XrayScatter(Lx, params=params) if "params" in mors.XrayScatter.__code__.co_varnames else mors.XrayScatter(Lx)
```
*(If `XrayScatter` does not accept `params` directly, set `sigmaXray` via your model run parameters and use it consistently.)*

---

### Step 4: Get detailed diagnostics (`ExtendedQuantities`)

If you need additional model internals (stellar properties, wind quantities, magnetic fields, torques), call `ExtendedQuantities` with envelope and core rotation rates:

```python
import mors

q = mors.ExtendedQuantities(Mstar=1.0, Age=5000.0, OmegaEnv=10.0, OmegaCore=10.0)
print(list(q))
```

---

### Common pitfalls
- Don’t mix `Omega` (Ω☉) and `Prot` (days) in the same call; pick one rotation representation.
- Scatter functions are random; results differ each run unless you control the random seed in your workflow.
- `ExtendedQuantities` requires **both** `OmegaEnv` and `OmegaCore`.
95 changes: 95 additions & 0 deletions docs/How-to/activity_timelines.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
## Activity timelines (How-to)

### Goal
Compute how long a star (or a cluster of stars) stays above an activity threshold (e.g., X-ray luminosity) or how long it remains in the saturated regime.

### Prerequisites
Install MORS and download stellar evolution data:

```bash
pip install fwl-mors
mors download all
```

> **Units:** thresholds must match the quantity you use (e.g., `Lx` in **erg s⁻¹**). Ages returned are in **Myr**.

---

### Compute activity threshold

#### Option 1: Use the standalone helper on an explicit track (basic)

`mors.ActivityLifetime` takes an age array and a track array and returns the age when the track finally drops below the threshold.

```python
import mors

AgeThreshold = mors.ActivityLifetime(
Age=star.AgeTrack,
Track=star.LxTrack,
Threshold=1.0e28
)
print(AgeThreshold)
```

**Behavior:**
- If the track crosses the threshold multiple times → returns the **final** crossing time.
- If it never goes below the threshold → returns the **final age** in the track.
- If it is never above the threshold → returns `0.0`.

Optional: limit the search to ages below `AgeMax`.

---

#### Option 2: Prefer the `Star.ActivityLifetime` method (recommended)

This version selects the track internally; you only provide the quantity name and threshold.

```python
AgeThreshold = star.ActivityLifetime(Quantity="Lx", Threshold=1.0e28)
print(AgeThreshold)
```

You can also set a maximum age to consider:

```python
AgeThreshold = star.ActivityLifetime(Quantity="Lx", Threshold=1.0e28, AgeMax=1000.0)
```

**Note**: The `Quantity` string must match one of the supported activity tracks:

- `Lx`, `Fx`, `Rx`, `FxHZ`
- `Leuv1`, `Feuv1`, `Reuv1`, `Feuv1HZ`
- `Leuv2`, `Feuv2`, `Reuv2`, `Feuv2HZ`
- `Leuv`, `Feuv`, `Reuv`, `FeuvHZ`
- `Lly`, `Fly`, `Rly`, `FlyHZ`

---

### Compute cluster lifetimes (same interface, returns arrays)

For a `mors.Cluster`, the call is the same but returns one value per star:

```python
AgeThreshold = cluster.ActivityLifetime(Quantity="Lx", Threshold=1.0e28)
print(AgeThreshold) # numpy array
```

---

### Compute saturation lifetime

To get the time the star remains in the saturated regime, pass `Threshold="sat"`:

```python
AgeSaturated = star.ActivityLifetime(Quantity="Lx", Threshold="sat")
print(AgeSaturated)
```

Note: the choice of `Quantity` should not change the saturation lifetime (XUV quantities saturate together), but it must be a valid option.

---

### Common pitfalls
- Make sure your `Threshold` uses the same units as the chosen `Quantity` (e.g., `Lx` is erg s⁻¹).
- The function returns the **final** threshold crossing time if there are multiple crossings.
Loading