Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ENH: Improve LineParameters and RLF compatibility #1

Merged
merged 3 commits into from
Nov 29, 2024
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
7 changes: 7 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
version: 2
updates:
- package-ecosystem: "github-actions"
directory: "/"
target-branch: "develop"
schedule:
interval: "monthly"
80 changes: 80 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
name: CI

on:
push:
branches: [main, develop]
tags:
- "*"
paths-ignore:
- "doc/**"
- ".vscode/**"
pull_request:
branches: [main, develop]
paths-ignore:
- "doc/**"
- ".vscode/**"

env:
CI: true
UV_SYSTEM_PYTHON: 1

jobs:
build:
runs-on: ubuntu-latest

strategy:
matrix:
python-version: ["3.10", "3.11", "3.12", "3.13"]

steps:
- uses: actions/checkout@v4
with:
lfs: false

- name: Create LFS file list
run: git lfs ls-files -l | cut -d' ' -f1 | sort > .lfs-assets-id

- name: Cache git LFS
uses: actions/cache@v4
with:
path: .git/lfs
key: git-lfs-v1-${{ matrix.python-version }}-${{ hashFiles('.lfs-assets-id') }}
restore-keys: |
git-lfs-v1-${{ matrix.python-version }}
git-lfs-v1
git-lfs

- name: Git LFS
run: |
git lfs checkout
git lfs pull
git lfs prune --verify-remote

- name: Install uv
uses: astral-sh/setup-uv@v3
with:
enable-cache: true

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
uv sync --frozen --all-extras
env:
GIT_LFS_SKIP_SMUDGE: 1

- name: Test with pytest
run: |
uv run pytest -vv -n=auto --durations=25 --cov-report html --cov-config pyproject.toml roseau
env:
ROSEAU_LOAD_FLOW_LICENSE_KEY: ${{ secrets.ROSEAU_LOAD_FLOW_LICENSE_KEY }}

- name: Archive code coverage results
uses: actions/upload-artifact@v4
if: ${{ always() }}
with:
name: code-coverage-report-${{ runner.os }}-python-${{ matrix.python-version }}
path: htmlcov/
27 changes: 27 additions & 0 deletions .github/workflows/pre-commit.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: pre-commit

on:
push:
branches: [main, develop]
paths-ignore:
- ".vscode/**"
- ".idea/**"
tags:
- "*"
pull_request:
branches: [main, develop]
paths-ignore:
- ".vscode/**"
- ".idea/**"

jobs:
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
lfs: false
- uses: actions/setup-python@v5
with:
python-version: "3.13"
- uses: pre-commit/[email protected]
11 changes: 3 additions & 8 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/astral-sh/uv-pre-commit
rev: 0.4.30
rev: 0.5.4
hooks:
- id: uv-lock
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.7.2
rev: v0.8.0
hooks:
- id: ruff
args: [--fix]
Expand All @@ -27,13 +27,8 @@ repos:
files: ^doc/.*\.md$
args: [-l 90]
- repo: https://github.com/rbubley/mirrors-prettier
rev: v3.3.3
rev: v3.4.0
hooks:
- id: prettier
args: ["--print-width", "120"]
require_serial: true
- repo: https://github.com/cmhughes/latexindent.pl
rev: V3.24.4
hooks:
- id: latexindent
args: [-l, -m, -s, -wd]
12 changes: 4 additions & 8 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,12 @@ authors = [
{ name = "Sébastien Vallet", email = "[email protected]" },
{ name = "Benoît Vinot", email = "[email protected]" },
{ name = "Florent Cadoux", email = "[email protected]" },
{ name = "Louise Muller", email = "[email protected]" },
{ name = "Victor Gouin" },
]
maintainers = [
{ name = "Ali Hamdan", email = "[email protected]" },
]
readme = "README.md"
license = { file = "LICENSE.md" }
classifiers = [
"Development Status :: 3 - Alpha",
# "License :: OSI Approved :: The 3-Clause BSD License (BSD-3-Clause)", # https://github.com/pypa/trove-classifiers/issues/70
Expand Down Expand Up @@ -58,12 +57,14 @@ dev = [
"pytest-cov>=5.0.0",
"pytest-xdist>=3.1.0",
"coverage[toml]>=7.0.5",
"coverage-conditional-plugin>=0.9.0",
]

[tool.uv]
managed = true

[tool.uv.sources]
roseau-load-flow = { git = "https://github.com/RoseauTechnologies/Roseau_Load_Flow.git", branch = "develop" }

[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
Expand Down Expand Up @@ -97,11 +98,6 @@ docstring-code-format = true
[tool.coverage.run]
branch = true
omit = ["roseau/load_flow_single/__about__.py"]
plugins = ["coverage_conditional_plugin"]

[tool.coverage.coverage_conditional_plugin.rules]
no-cover-if-py-gte-311 = "sys_version_info >= (3, 11)"
no-cover-if-py-lt-311 = "sys_version_info < (3, 11)"

[tool.coverage.paths]
source = ["roseau/load_flow_single/"]
Expand Down
15 changes: 15 additions & 0 deletions roseau/load_flow_single/__about__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
__authors__ = ", ".join(
(
"Ali Hamdan <[email protected]>",
"Sébastien Vallet <[email protected]>",
"Benoît Vinot <[email protected]>",
"Florent Cadoux <[email protected]>",
)
)
__copyright__ = "Roseau Technologies 2018"
__credits__ = "Roseau Technologies"
__license__ = "BSD-3-Clause"
__maintainer__ = "Ali Hamdan"
__email__ = "[email protected]"
__status__ = "In development"
__url__ = "https://github.com/RoseauTechnologies/Roseau_Load_Flow_Single/"
54 changes: 54 additions & 0 deletions roseau/load_flow_single/__init__.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,20 @@
import importlib.metadata

from roseau.load_flow import exceptions, license, show_versions, testing, typing, units, utils
from roseau.load_flow.exceptions import RoseauLoadFlowException, RoseauLoadFlowExceptionCode
from roseau.load_flow.license import License, activate_license, deactivate_license, get_license
from roseau.load_flow.units import Q_, ureg
from roseau.load_flow.utils import Insulator, LineType, Material, constants
from roseau.load_flow_single.__about__ import (
__authors__,
__copyright__,
__credits__,
__email__,
__license__,
__maintainer__,
__status__,
__url__,
)
from roseau.load_flow_single.models.branches import AbstractBranch
from roseau.load_flow_single.models.buses import Bus
from roseau.load_flow_single.models.core import Element
Expand All @@ -16,7 +33,19 @@
from roseau.load_flow_single.models.transformers import Transformer, TransformerParameters
from roseau.load_flow_single.network import ElectricalNetwork

__version__ = importlib.metadata.version("roseau-load-flow-single")

__all__ = [
# RLFS elements
"__authors__",
"__copyright__",
"__credits__",
"__email__",
"__license__",
"__maintainer__",
"__status__",
"__url__",
"__version__",
"Element",
"Line",
"LineParameters",
Expand All @@ -34,4 +63,29 @@
"Projection",
"Control",
"AbstractBranch",
# Other imports from RLF to have the same interface
# utils
"Insulator",
"LineType",
"Material",
"utils",
"constants",
# License
"License",
"activate_license",
"deactivate_license",
"get_license",
"license",
# Units
"Q_",
"units",
"ureg",
# Exceptions
"RoseauLoadFlowException",
"RoseauLoadFlowExceptionCode",
"exceptions",
# Other
"show_versions",
"testing",
"typing",
]
90 changes: 67 additions & 23 deletions roseau/load_flow_single/conftest.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import importlib
import inspect
import os
from pathlib import Path

import platformdirs
import pytest
from _pytest.monkeypatch import MonkeyPatch

from roseau.load_flow import activate_license
import roseau
from roseau.load_flow.utils.log import set_logging_config

HERE = Path(__file__).parent.expanduser().absolute()
Expand All @@ -30,27 +32,6 @@
]


@pytest.fixture(autouse=True, scope="session")
def _log_setup():
"""A basic fixture (automatically used) to set the log level"""
set_logging_config(verbosity="debug")


@pytest.fixture(autouse=True, scope="session")
def _license_setup(_log_setup, tmp_path_factory):
"""A basic fixture (automatically used) to activate a license for the tests"""
license_folderpath = tmp_path_factory.mktemp("roseau-test")

def _user_cache_dir():
return str(license_folderpath)

mpatch = MonkeyPatch()
mpatch.setattr(target=platformdirs, name="user_cache_dir", value=_user_cache_dir)
activate_license(key=None) # Use the environment variable `ROSEAU_LOAD_FLOW_LICENSE_KEY`
yield
mpatch.undo()


@pytest.fixture(params=["impedance", "power"], ids=["impedance", "power"])
def network_load_data_name(request) -> str:
return request.param
Expand All @@ -74,3 +55,66 @@ def test_networks_path() -> Path:
@pytest.fixture(params=THREE_PHASES_TRANSFORMER_TYPES, ids=THREE_PHASES_TRANSFORMER_TYPES)
def three_phases_transformer_type(request) -> str:
return request.param


@pytest.fixture(autouse=True)
def patch_engine(request):
mpatch = MonkeyPatch()

if "no_patch_engine" in request.keywords:
# A load flow must be solved in the test
# Skip if no license key in the environment
if os.getenv("ROSEAU_LOAD_FLOW_LICENSE_KEY") is None: # pragma: no-cover
pytest.skip(
reason="This test requires a license key. Please set ROSEAU_LOAD_FLOW_LICENSE_KEY in your environment."
)

# Activate logging
set_logging_config("debug")
else:
# Patch the engine

class Foo:
def __init__(self, *args, **kwargs): # Accept all constructor parameters
pass

def __getattr__(self, attr): # Accept all methods
if attr.startswith("__array"): # Let numpy interface
return object.__getattr__(self, attr)
else:
return self.foo

def foo(self, *args, **kwargs):
pass

def bar(*args, **kwargs): # pragma: no-cover
pass

# Get all roseau.load_flow_single and roseau.load_flow submodules
for dp in (Path(roseau.load_flow_single.__file__).parent, Path(roseau.load_flow.__file__).parent):
relative_to = dp.parents[1]
for dirpath, _, filenames in os.walk(dp): # TODO In Python 3.12 use rlf_directory_path.walk()
dirpath = Path(dirpath) # TODO Useless in Python 3.12
for p in dirpath.parts:
if p in {"tests", "__pycache__", "data"}:
break
else:
base_module = str(dirpath.relative_to(relative_to)).replace("/", ".")
for f in filenames:
if not f.endswith(".py"):
continue
module = importlib.import_module(f"{base_module}.{f.removesuffix('.py')}")
for _, klass in inspect.getmembers(
module,
lambda member: inspect.isclass(member)
and "load_flow_engine." in member.__module__
and member.__name__.startswith("Cy")
and member.__name__ != "CyLicense", # Test of the static methods of this class
):
mpatch.setattr(f"{module.__name__}.{klass.__name__}", Foo)

# Also patch the activate license function of the _solvers module
mpatch.setattr("roseau.load_flow.license.cy_activate_license", bar)

yield mpatch
mpatch.undo()
Loading
Loading