Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
d059c2d
bump dev version
gtrevisan Mar 13, 2025
ec215b0
tolerate critical mdsplus errors (#418)
gtrevisan Mar 20, 2025
576fe19
remove drafts scripts (#420)
gtrevisan Mar 20, 2025
ec31513
expose efit tree and time base to cli settings (#419)
gtrevisan Mar 20, 2025
e197412
make shot message formatter lazy (#421)
gtrevisan Mar 31, 2025
751058d
drop commit hash (#422)
gtrevisan Mar 31, 2025
7e18320
Ship command-line entry point with package (#425)
gtrevisan Apr 1, 2025
ea5972a
Drop cache setting (#423)
gtrevisan Apr 2, 2025
92a9231
Simplify output settings (#426)
gtrevisan Apr 2, 2025
7200a60
Fix non-git edge cases (#429)
gtrevisan Apr 3, 2025
60aebe6
Better exceptions (#428)
gtrevisan Apr 3, 2025
8f52281
Drop scikit learn (#430)
gtrevisan Apr 3, 2025
09ef147
Cleanup old code (#431)
gtrevisan Apr 7, 2025
76f9bc0
Apply some fixes by sourcery (#432)
gtrevisan Apr 7, 2025
c77e833
Update deps to Mar 2025 (#435)
gtrevisan Apr 7, 2025
3387241
increase number of lines for similarity (#437)
gtrevisan Apr 8, 2025
f7cf800
fix hardcoded trees (#436)
gtrevisan Apr 9, 2025
48131da
Copy changes from wei/improve-get-ohmic (#416)
yumouwei Apr 9, 2025
123f1ad
Improve docstring of DIII-D physics methods (#424)
yumouwei Apr 9, 2025
2162397
Add isort to lint matrix (#438)
gtrevisan Apr 9, 2025
3900773
More robust shot data retrieval (#434)
gtrevisan Apr 14, 2025
e414215
simplify log_settings (#441)
gtrevisan Apr 16, 2025
17589bf
Do not publish dev package (#447)
gtrevisan Apr 17, 2025
0a17e7f
Change author order (#448)
gtrevisan Apr 23, 2025
3c4ed8d
Refactor get_shape_parameters (#450)
yumouwei Apr 28, 2025
c022623
Tweak readme (#449)
gtrevisan May 2, 2025
074a9e4
Revamp internals and outputs to use xarray (#442)
gtrevisan May 5, 2025
c504ce0
Update deps to Apr 2025 (#453)
gtrevisan May 5, 2025
1f955a6
Improve EAST physics methods (#411)
yumouwei May 5, 2025
9e2c8bd
disable workflow dispatch (#454)
gtrevisan May 6, 2025
cb244c7
Improve time settings (#439)
yumouwei May 7, 2025
ebf0654
bump version to v0.11.0
gtrevisan May 7, 2025
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
4 changes: 4 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,10 @@ jobs:
- name: Test import
run: poetry run python -c "import disruption_py"

- name: Discard dev package
if: github.event_name == 'release'
run: "! poetry version | grep dev"

- name: Build package
run: poetry build

Expand Down
1 change: 0 additions & 1 deletion .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ on:
branches:
- main
- dev
workflow_dispatch:

permissions:
contents: read
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ on:
branches:
- main
- dev
workflow_dispatch:

jobs:

Expand All @@ -24,7 +23,7 @@ jobs:
github.event.pull_request.draft == false
strategy:
matrix:
tool: [black, pylint, ruff, toml-sort, yamllint]
tool: [black, isort, pylint, ruff, toml-sort, yamllint]
name: ${{ matrix.tool }}
steps:
- uses: actions/checkout@v4
Expand Down
3 changes: 1 addition & 2 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ on:
branches:
- main
- dev
workflow_dispatch:

jobs:

Expand Down Expand Up @@ -64,7 +63,7 @@ jobs:

- name: Setup SSH key
run: |
mkdir ~/.ssh
mkdir -p ~/.ssh
echo "${{ secrets.SSH_TUNNEL }}" \
| tee ~/.ssh/id_rsa \
| sha256sum
Expand Down
110 changes: 106 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ DisruptionPy makes it easy to retrieve experimental data from [MDSplus](https://
Users can create their own routines and/or use built-in ones that retrieve and derive a variety of important signals from experimental data for disruption analysis.
These routines are then interpolated on a requested timebase across the specified set of plasma discharges (or shots) to assemble a dataset and save it under a variety of available formats.

<p align="center">
<img src="docs/workflow.png" alt="Schematic flowchart of a typical DisruptionPy workflow. By Y Wei (2024)" width="400" onerror="this.onerror=null;this.src='workflow.png';" />
</p>

_Figure: Schematic flowchart of a typical DisruptionPy workflow. By Y Wei (2024) [[6](https://meetings.aps.org/Meeting/DPP24/Session/PP12.10)]._

Expand Down Expand Up @@ -91,7 +93,6 @@ Brief description of the folders in our project:

- `disruption_py/`, package source code,
- `docs/`, documentation sources,
- `drafts/`, experimental scripts,
- `examples/`, example workflows,
- `scripts/`, miscellaneous scripts,
- `tests/`, testing workflows.
Expand All @@ -116,17 +117,118 @@ pip install disruption-py

For custom installations, please refer to our [Installation guide](docs/INSTALL.md).

Starting with [v0.11](https://github.com/MIT-PSFC/disruption-py/releases/tag/v0.11), we support execution through [on-the-fly virtual environment creation with `uv`](https://docs.astral.sh/uv/guides/tools/):

```bash
uvx disruption-py
```


## Getting Started

Please see the [project quickstart](https://mit-psfc.github.io/disruption-py/quickstart/usage_quickstart/).
When installed, a simple command-line entry point is available as `disruption-py`.

The command-line arguments, which are subject to change, are documented in the help message:

```bash
disruption-py --help
```
```
usage: disruption-py [-h] [-t TOKAMAK] [-m METHODS] [-e EFIT_TREE] [-b TIME_BASE]
[-o OUTPUT_FILE] [-p PROCESSES] [-l LOG_LEVEL]
[shots ...]

positional arguments:
shots

options:
-h, --help show this help message and exit
-t TOKAMAK, --tokamak TOKAMAK
-m METHODS, --methods METHODS
-e EFIT_TREE, --efit-tree EFIT_TREE
-b TIME_BASE, --time-base TIME_BASE
-o OUTPUT_FILE, --output-file OUTPUT_FILE
-p PROCESSES, --processes PROCESSES
-l LOG_LEVEL, --log-level LOG_LEVEL
```

A typical command-line invocation of the entry point would be:

```bash
# fetch EFIT-based parameters for a couple of 2015 Alcator C-MOD shots
disruption-py -m get_efit_parameters -o efit.csv 1150805012 1150805020
```

For simplified workflows, a flattened invocation of our data pipeline is available from Python, as well:

```python
from disruption_py.workflow import run
out = run(*args)
```

For more complicated workflows requiring the configuration of all the settings according to the specific user needs, a full-fledged disruption script might be necessary.
Please refer to our `examples/defaults.py` script for a quickstart workflow with explicit default arguments.


## Configuration

DisruptionPy itself does not provide access to any of the underlying servers.

While we honor the legacy `sybase_login` file credential format for database connections, we recommend using the following configuration snippet for maximum flexibility:

```toml
# ~/.config/disruption-py/config.toml

[cmod.inout.sql]
db_user = ""
db_pass = ""

[d3d.inout.sql]
db_user = ""
db_pass = ""

[east.inout.sql]
db_user = ""
db_pass = ""
```

Any configuration parameter can be overridden by the above configuration file.


## Contributing

> [!IMPORTANT]
> Make sure you refer to the latest version of our [development branch](https://github.com/MIT-PSFC/disruption-py/tree/dev)!
Make sure you refer to the latest version of our [development branch](https://github.com/MIT-PSFC/disruption-py/tree/dev)!

- If you encounter any problems, please [create a new issue](https://github.com/MIT-PSFC/disruption-py/issues/new).
- If you would like to contribute, please [submit a pull request](https://github.com/MIT-PSFC/disruption-py/compare/dev...).
- If you have general questions, please [start a new discussion](https://github.com/MIT-PSFC/disruption-py/discussions/new?category=q-a).


## Credits

#### before 2021

The backbone material for this project, that is, the original MATLAB code, was authored by several contributors at [MIT PSFC](https://www.psfc.mit.edu/) before 2021:

- Robert Granetz, Principal Research Scientist,
- Cristina Rea, then Research Scientist,
- Kevin Montes, then Graduate Research Assistant,
- Alex Tinguely, then Graduate Research Assistant,
- Jinxiang Zhu, then Graduate Research Assistant.

#### 2022 - 2023

The initial porting of the code to Python, under the supervision of Dr. Cristina Rea, was tackled by:

- Herbert Turner, then Master's Student.

#### 2024 - present

The subsequent heavy development and maintenance of the code within the newly-established [MIT PSFC Disruption Studies Group](https://disruptions.mit.edu/), was funded under the 3-year DOE FES Grant ["Open and FAIR Fusion for Machine Learning Applications"](https://crea-psfc.github.io/open-fair-fusion/) (2024-2026).

Several contributors have been involved in the development of the code since then, most notably:

- Gregorio L. Trevisan, Research Scientist,
- Josh Lorincz, Undergraduate Student,
- Amos Decker, Undergraduate Student,
- William Wei, PostDoctoral Associate.
12 changes: 3 additions & 9 deletions disruption_py/config.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,7 @@
[default]
max_processes = 10
max_shot_time = 7.0 # [s]
time_const = 1e-6

[default.inout.sql]
protected_columns = ["shot", "time"]
write_database_table_name = "disruption_warning_test"

[default.tests]
match_fraction = 0.95 # Fraction of signals that must match between MDSplus and SQL
val_tolerance = 0.01 # Tolerance for comparing values between MDSplus and SQL
verbose_output = true

[default.time]
time_const = 1e-6
52 changes: 1 addition & 51 deletions disruption_py/core/physics_method/caching.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,9 @@

import functools
import threading
from typing import Callable, List
from typing import Callable

import numpy as np
import pandas as pd

from disruption_py.core.physics_method.params import PhysicsMethodParams

Expand Down Expand Up @@ -89,52 +88,3 @@ def get_method_cache_key(
len(times),
hashable_other_params,
)


def manually_cache(
physics_method_params: PhysicsMethodParams,
data: pd.DataFrame,
method: Callable,
method_name: str,
method_columns: List[str],
) -> bool:
"""
Manually cache results based on the provided DataFrame and method details.

Parameters
----------
physics_method_params : PhysicsMethodParams
The parameters containing the shot ID and logger for logging.
data : pd.DataFrame
The DataFrame containing the data to be cached.
method : Callable
The method for which the results are being cached.
method_name : str
The name of the method being cached, used for logging.
method_columns : List[str]
The list of columns to check and cache.

Returns
-------
bool
True if caching was successful, False if there were missing columns.
"""
if method_columns is None:
return False
if not hasattr(physics_method_params, "cached_results"):
physics_method_params.cached_results = {}
missing_columns = set(col for col in method_columns if col not in data.columns)
if len(missing_columns) == 0:
cache_key = get_method_cache_key(method, data["time"].values)
physics_method_params.cached_results[cache_key] = data[method_columns]
physics_method_params.logger.debug(
"Manually caching {method_name}",
method_name=method_name,
)
return True
physics_method_params.logger.debug(
"Can not cache {method_name} missing columns {missing_columns}",
method_name=method_name,
missing_columns=missing_columns,
)
return False
4 changes: 2 additions & 2 deletions disruption_py/core/physics_method/decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ def physics_method(
"""
Decorator to signify a method to be run by DisruptionPy.

The decorated method calculates disruption parameters and returns a Pandas
DataFrame. All decorated methods must take the single argument params of type
The decorated method calculates disruption parameters and returns a Dataset.
All decorated methods must take the single argument params of type
`PhysicsMethodParams`. The decorated method will be run if designated by the
`run_methods` or `run_columns` attributes of the `RetrievalSettings`
class, and if included inside of the `custom_physics_methods` argument of the
Expand Down
2 changes: 1 addition & 1 deletion disruption_py/core/physics_method/metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ def bind(
# Utility methods for decorated methods


def is_parametered_method(method: Callable) -> bool:
def is_physics_method(method: Callable) -> bool:
"""
Returns whether the method is decorated with `physics_method` decorator

Expand Down
37 changes: 23 additions & 14 deletions disruption_py/core/physics_method/params.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,13 @@
"""
Module for defining parameters used in physics methods for DisruptionPy.
"""

from dataclasses import dataclass, field
from typing import Any, Dict
from typing import Any, Dict, Tuple

import numpy as np
import pandas as pd
from loguru import logger

from disruption_py.core.utils.misc import shot_log_msg
from disruption_py.core.utils.misc import shot_msg_patch, to_tuple
from disruption_py.inout.mds import MDSConnection
from disruption_py.machine.tokamak import Tokamak

Expand All @@ -28,17 +26,9 @@ class PhysicsMethodParams:
disruption_time: float
mds_conn: MDSConnection
times: np.ndarray
cache_data: pd.DataFrame
pre_filled_shot_data: pd.DataFrame
interpolation_method: Any # Fix
metadata: dict

def __post_init__(self):
self.logger = logger.patch(
lambda record: record.update(
message=shot_log_msg(self.shot_id, record["message"])
)
)
self.logger = shot_msg_patch(logger, self.shot_id)

cached_results: Dict[str, Any] = field(default_factory=dict)

Expand All @@ -55,7 +45,26 @@ def disrupted(self) -> bool:
return self.disruption_time is not None

def cleanup(self) -> None:
"""Clean up resources used by the physics method parameters."""
"""
Clean up resources used by the physics method parameters.
"""
self.mds_conn.cleanup()
self.times = None
self.cached_results.clear()

def to_coords(self) -> Dict[str, Tuple[str, np.ndarray]]:
"""
Create a dictionary of coordinates based on the parameters.

Returns
-------
Dict[str, Tuple[str, np.ndarray]]
A dictionary with `shot` and `time` as coordinates for dimension `idx`.
"""
return to_tuple(
data={
"shot": len(self.times) * [self.shot_id],
"time": self.times,
},
dim="idx",
)
Loading