Skip to content
Open
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
6 changes: 3 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ jobs:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
# Test with the earliest and the latest python versions supported
python-version: ["3.9", "3.13"]
python-version: ["3.11", "3.14"]

steps:
- uses: actions/checkout@v6
Expand All @@ -40,7 +40,7 @@ jobs:
--cov-report=xml

- name: Upload coverage to Codecov
if: success() && (github.event_name == 'push' && runner.os == 'Linux' && matrix.python-version == 3.9)
if: success() && (github.event_name == 'push' && runner.os == 'Linux' && matrix.python-version == 3.11)
uses: codecov/codecov-action@v5
with:
fail_ci_if_error: true
Expand All @@ -59,7 +59,7 @@ jobs:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
# Test with the earliest and the latest python versions supported
python-version: ["3.9", "3.13"]
python-version: ["3.11", "3.14"]

steps:
- uses: actions/checkout@v6
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/documentation.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ jobs:
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: 3.13
python-version: 3.14
- name: Install dependencies
run: |
sudo apt update -y
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ jobs:
fail-fast: false
matrix:
os: [windows-latest]
python-version: ["3.9"]
python-version: ["3.11"]

steps:
- uses: actions/checkout@v6
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/regression_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
# Test with the earliest and the latest python versions supported
python-version: ["3.9", "3.13"]
python-version: ["3.11", "3.14"]

steps:
- uses: actions/checkout@v6
Expand Down
2 changes: 1 addition & 1 deletion .readthedocs.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ version: 2
build:
os: ubuntu-20.04
tools:
python: "3.9"
python: "3.11"
apt_packages:
- graphviz
- pandoc
Expand Down
30 changes: 15 additions & 15 deletions docs/installation/pipx-based.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pipx-based installation
To help you installing MUSE in your system we will follow these steps:

- `Launching a terminal`_: Needed to both install and run MUSE.
- `Installing a compatible Python version`_: MUSE works with Python 3.9 to 3.13.
- `Installing a compatible Python version`_: MUSE works with Python 3.11 to 3.14.
- `Installing pipx`_: A Python application manager that facilitates installing, keeping applications updated and run them in their own isolated environments.
- `Installing MUSE itself`_

Expand All @@ -22,8 +22,8 @@ In the following sections, we will guide you step by step in configuring your sy

.. code-block::

pyenv install 3.9.13
pyenv shell 3.9.13
pyenv install 3.11.0
pyenv shell 3.11.0
python -m pip install pipx
python -m pipx ensurepath
python -m pipx install muse-os
Expand Down Expand Up @@ -67,7 +67,7 @@ Once you have launched the Terminal, the window that opens will show the command
Installing a compatible Python version
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

MUSE needs Python to run and it works with versions 3.9 to 3.13, so the next step is to install a suitable version of Python.
MUSE needs Python to run and it works with versions 3.11 to 3.14, so the next step is to install a suitable version of Python.

.. note::

Expand All @@ -92,7 +92,7 @@ The first thing will be to check if you already have a suitable python version i

python --version

If the output is ``Python 3.Y.X`` or ``Python 3.Y.X``, where ``X`` is any number and ``Y`` is 9, 10, 11 or 12, then **you have a version of Python compatible with MUSE and you can skip this section altogether**. Move to `Installing pipx`_. In any other case, keep reading.
If the output is ``Python 3.Y.X`` or ``Python 3.Y.X``, where ``X`` is any number and ``Y`` is 11, 12, 13 or 14, then **you have a version of Python compatible with MUSE and you can skip this section altogether**. Move to `Installing pipx`_. In any other case, keep reading.

There are multiple ways of installing Python, as well as multiple distributions. Here we have opted for the one that we believe is simplest, requires the smallest downloads and gives the maximum flexibility: using ``pyenv``.

Expand Down Expand Up @@ -189,38 +189,38 @@ With ``pyenv`` installed and correctly configured, it is now easy to install any

pyenv install -l

You should see a long list of versions to choose from. Let's install one of the later versions of the 3.9 family:
You should see a long list of versions to choose from. Let's install 3.11.0 as an example:

.. code-block:: bash

pyenv install 3.9.13
pyenv install 3.11.0

The command will take a minute or two to complete, depending on your internet connection, and show an output similar to the following (this is an example from Windows):

.. code-block:: output

:: [Info] :: Mirror: https://www.python.org/ftp/python
:: [Downloading] :: 3.9.13 ...
:: [Downloading] :: From https://www.python.org/ftp/python/3.9.13/python-3.9.13-amd64.exe
:: [Downloading] :: To C:\Users\your_username\.pyenv\pyenv-win\install_cache\python-3.9.13-amd64.exe
:: [Installing] :: 3.9.13 ...
:: [Info] :: completed! 3.9.13
:: [Downloading] :: 3.11.0 ...
:: [Downloading] :: From https://www.python.org/ftp/python/3.11.0/python-3.11.0-amd64.exe
:: [Downloading] :: To C:\Users\your_username\.pyenv\pyenv-win\install_cache\python-3.11.0-amd64.exe
:: [Installing] :: 3.11.0 ...
:: [Info] :: completed! 3.11.0

Now, we have a new Python version in our system, but it is still not available (if you run ``python --version`` you will get the same result as before). There are two options moving forward:

- If you want to set it as the global python version, available system wide (only do this if you really want to set is as your main Python!) run:

.. code-block:: bash

pyenv global 3.9.13
pyenv global 3.11.0

- If you just want it momentarily to install MUSE run instead the following command:

.. code-block:: bash

pyenv shell 3.9.13
pyenv shell 3.11.0

In both cases, if you run ``python --version`` afterwards, you should get ``Python 3.9.13``.
In both cases, if you run ``python --version`` afterwards, you should get ``Python 3.11.0``.

Installing ``pipx``
~~~~~~~~~~~~~~~~~~~
Expand Down
4 changes: 2 additions & 2 deletions docs/installation/virtual-env-based.rst
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ To create an environment called ``muse_env`` run:

.. code-block:: bash

conda create -n muse_env python=3.9
conda create -n muse_env python=3.11

Now, you can activate the environment with:

Expand Down Expand Up @@ -95,7 +95,7 @@ Creating a virtual environment with ``pyenv + venv``

Alternatively to creating virtual environments in ``conda``, you can also make use of two well-tested and maintained libraries.
We met the first one, ``pyenv``, already in the :ref:`pipx-based <pipx-based>` under the section :ref:`Installing pyenv <pipx-based-installing-pyenv>` and the installation procedure is exactly the same.
If you go down that route, please follow the steps outlined there and chose a recent version ``Python``, say 3.9.
If you go down that route, please follow the steps outlined there and chose a recent version ``Python``, say 3.13.

The second package we need to create virtual environments for any specific ``Python`` version is called
``venv``, and it ships with ``Python`` by default. To create such an environment, we first need to ensure that the
Expand Down
13 changes: 6 additions & 7 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,24 +11,23 @@ name = "MUSE_OS"
description = "Energy System Model"
readme = "README.md"
license = {file = "LICENSE"}
requires-python = ">= 3.9, <3.14"
requires-python = ">= 3.11, <3.15"
keywords = ["energy", "modelling"]
classifiers = [
"Development Status :: 4 - Beta",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Programming Language :: Python :: 3.13",
"Programming Language :: Python :: 3.14",
"Intended Audience :: Science/Research",
"Intended Audience :: Other Audience",
"License :: OSI Approved :: GNU General Public License v3 (GPLv3)"
]
dependencies = [
"numpy>=2.0",
"scipy>=1.13",
"pandas>=2.2,<3.0",
"xarray>=2024.6,<=2024.11",
"numpy>=2.4,<3.0",
"scipy>=1.17,<2.0",
"pandas>=3.0,<4.0",
"xarray>=2026.2",
"bottleneck>=1.4",
"coloredlogs",
"toml",
Expand Down
8 changes: 3 additions & 5 deletions src/muse/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -489,9 +489,7 @@ def initialize_from_assets(
if "asset" not in agent.assets.dims or len(agent.assets.asset) == 0:
return replacement

assets = (
xr.ones_like(reduce_assets(agent.assets.asset, coords=coords), dtype=bool)
.rename(technology="asset")
.set_index()
)
assets = xr.ones_like(
reduce_assets(agent.assets.asset, coords=coords), dtype=bool
).set_index(asset="technology")
return (assets * replacement).transpose("asset", "replacement")
6 changes: 4 additions & 2 deletions src/muse/readers/csv.py
Original file line number Diff line number Diff line change
Expand Up @@ -679,7 +679,7 @@ def process_technologies(
)

# Merge inputs/outputs with technodata
technodata = technodata.merge(outs).merge(ins)
technodata = technodata.merge(outs, join="outer").merge(ins, join="outer")

# Merge technodata_timeslices if provided. This will prioritise values defined in
# technodata_timeslices, and fallback to the non-timesliced technodata for any
Expand All @@ -700,7 +700,9 @@ def process_technologies(
technodata = check_commodities(technodata, fill_missing=False)

# Add info about commodities
technodata = technodata.merge(COMMODITIES.sel(commodity=technodata.commodity))
technodata = technodata.merge(
COMMODITIES.sel(commodity=technodata.commodity), join="outer"
)

# Add commodity usage flags
technodata["comm_usage"] = (
Expand Down
2 changes: 1 addition & 1 deletion src/muse/readers/toml.py
Original file line number Diff line number Diff line change
Expand Up @@ -624,7 +624,7 @@ def read_technodata(
# Drop duplicate data vars before merging
common_vars = set(technologies.data_vars) & set(trade_data.data_vars)
technologies = technologies.drop_vars(common_vars)
technologies = technologies.merge(trade_data)
technologies = technologies.merge(trade_data, join="outer")

technologies = technologies.set_index(commodity="commodity") # See PR #638
return technologies
Expand Down
27 changes: 24 additions & 3 deletions src/muse/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,14 @@ def operation(x):

# Concatenate assets if a sequence is given
if not isinstance(assets, (xr.Dataset, xr.DataArray)):
assets = xr.concat(assets, dim=dim)
assets = xr.concat(
assets,
dim=dim,
join="outer",
coords="different",
compat="equals",
data_vars="all",
)
assert isinstance(assets, (xr.Dataset, xr.DataArray))

# If there are no assets, nothing needs to be done
Expand Down Expand Up @@ -370,7 +377,14 @@ def merge_assets(
capa_b_interp = interpolate_capacity(capa_b, year=years)

# Concatenate the two capacity arrays
result = xr.concat((capa_a_interp, capa_b_interp), dim=dimension)
result = xr.concat(
(capa_a_interp, capa_b_interp),
dim=dimension,
join="outer",
coords="different",
compat="equals",
data_vars="all",
)

#
forgroup = result.pipe(coords_to_multiindex, dimension=dimension)
Expand Down Expand Up @@ -583,7 +597,14 @@ def agent_concatenation(
)
else:
datum[name] = key
result = xr.concat(data.values(), dim=dim)
result = xr.concat(
data.values(),
dim=dim,
join="outer",
coords="different",
compat="equals",
data_vars="all",
)
if isinstance(result, xr.Dataset):
result = result.set_coords("agent")
if "year" in result.dims:
Expand Down
12 changes: 1 addition & 11 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -524,7 +524,7 @@ def drop_optionals(settings):

@fixture(autouse=True)
def warnings_as_errors(request):
from warnings import filterwarnings, simplefilter
from warnings import simplefilter

# disable fixture for some tests
if (
Expand All @@ -538,16 +538,6 @@ def warnings_as_errors(request):
simplefilter("error", DeprecationWarning)
simplefilter("error", PendingDeprecationWarning)

# The following warning is safe to ignore (raised by adhoc solver with Python 3.9)
# TODO: may be able to remove this once support for Python 3.9 is dropped
if request.module.__name__ == "test_fullsim_regression":
filterwarnings(
"ignore",
message="__array__ implementation doesn't accept a copy keyword",
category=DeprecationWarning,
module="xarray.core.variable",
)


@fixture
def save_registries():
Expand Down
9 changes: 8 additions & 1 deletion tests/test_demand_share.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,14 @@ def create_regional_market(technologies, stock):
usa_market = _matching_market(
broadcast_over_assets(technologies, usa_stock), usa_stock.capacity
)
market = xr.concat((asia_market, usa_market), dim="region")
market = xr.concat(
(asia_market, usa_market),
dim="region",
join="outer",
coords="different",
compat="equals",
data_vars="all",
)
return market, asia_stock, usa_stock


Expand Down
2 changes: 1 addition & 1 deletion tests/test_timeslice_output.py
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ def test_zero_utilization_factor_supply_timeslice(

zero_output = power_supply[
power_supply.timeslice.isin(zero_utilization_indices)
& (power_supply.technology == process_names)
& power_supply.technology.isin(process_names)
]
assert len(zero_output) == 0

Expand Down