Skip to content

Commit

Permalink
Initial work on System implementation
Browse files Browse the repository at this point in the history
It is not clear how to design those classes, though.
  • Loading branch information
aaschwanden committed Nov 6, 2023
1 parent 652afd0 commit 8cd8626
Show file tree
Hide file tree
Showing 5 changed files with 294 additions and 0 deletions.
1 change: 1 addition & 0 deletions pism_ragis/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,6 @@
"stats",
"computing",
"interpolation",
"systems",
"trajectories",
]
150 changes: 150 additions & 0 deletions pism_ragis/systems.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
# Copyright (C) 2023 Andy Aschwanden
#
# This file is part of pism-ragis.
#
# PISM-RAGIS is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# PISM-RAGIS is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License
# along with PISM; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

"""
Module provides System class
"""

from pathlib import Path
from typing import Union

import toml


class System:
"""
Class fo a system
"""

def __init__(self, d: Union[dict, Path, str]):
if isinstance(d, dict):
for key, value in d.items():
setattr(self, key, value)
elif isinstance(d, Path):
for key, value in toml.load(d).items():
setattr(self, key, value)
elif isinstance(d, str):
self._values = toml.loads(d)
else:
print(f"{d} not recognized")

def to_dict(self):
"""
Returns self as dictionary
"""
return self.__dict__

def __repr__(self):
repr_str = ""

repr_str += toml.dumps(self.to_dict())

return f"""
System
------------
{repr_str}
"""


class Systems:
"""
Class to hold Systems of base class System
"""

def __init__(self):
self._default_path: Path = Path("data/")
self.add_systems_from_path(self.default_path)

@property
def default_path(self):
"""
Return default path to glob for toml files
"""
return self._default_path

@default_path.setter
def default_path(self, value):
self._default_path = value

def __getitem__(self, name):
return self._values[name]

def __setitem__(self, key, value):
setattr(self, key, value)

def __iter__(self):
return iter(self._values)

def keys(self):
"""
Return keys
"""
return self._values.keys()

def items(self):
"""
Return items
"""
return self._values.items()

def values(self):
"""
Return values
"""
return self._values.values()

def add_system(self, system):
"""
Add a system from a System class
"""
system.to_dict()

def add_systems_from_path(self, path):
"""
Add systems from a pathlib.Path.
Use glob to add all files with suffix `toml`.
"""
p = Path(path).glob("*.toml")
sys = {}
for p_ in p:
s = toml.load(p_)
machine = s["machine"]
sys[machine] = s
self._values = sys

# def __len__(self):
# return len(self.systems)

def dump(self):
"""
Dump class to string
"""
repr_str = ""
for s in self.values():
repr_str += s["machine"]
repr_str += "\n------------\n\n"
repr_str += toml.dumps(s)
repr_str += "\n"
return repr_str

def __repr__(self):
return self.dump()
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,5 @@ pyogrio>=0.7.2
pyarrow>=14.0.0
tqdm>=4.66.1
openpyxl>=3.1.2
toml
types-toml
53 changes: 53 additions & 0 deletions tests/data/chinook.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
machine = "chinook"

[MPI]

mpido = "mpirun -np {cores} -machinefile ./nodes_$SLURM_JOBID"

[scheduler]

name = "SLRUM"
submit = "sbatch"
job_id = "SLURM_JOBID"

[filesystem]

work_dir = "SLURM_SUBMIT_DIR"

[queues]

t1standard = 24
t1small = 24
t2standard = 24
t2small = 24
debug = 24
analysis = 24

[job]

header = """#!/bin/sh
#SBATCH --partition={queue}
#SBATCH --ntasks={cores}
#SBATCH --tasks-per-node={ppn}
#SBATCH --time={walltime}
#SBATCH --mail-type=BEGIN
#SBATCH --mail-type=END
#SBATCH --mail-type=FAIL
#SBATCH --output=pism.%j
module list
umask 007
cd $SLURM_SUBMIT_DIR
# Generate a list of compute node hostnames reserved for this job,
# this ./nodes file is necessary for slurm to spawn mpi processes
# across multiple compute nodes
srun -l /bin/hostname | sort -n | awk '{{print $2}}' > ./nodes_$SLURM_JOBID
ulimit -l unlimited
ulimit -s unlimited
ulimit
"""
88 changes: 88 additions & 0 deletions tests/test_systems.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# Copyright (C) 2023 Andy Aschwanden
#
# This file is part of pism-ragis.
#
# PISM-RAGIS is free software; you can redistribute it and/or modify it under the
# terms of the GNU General Public License as published by the Free Software
# Foundation; either version 3 of the License, or (at your option) any later
# version.
#
# PISM-RAGIS is distributed in the hope that it will be useful, but WITHOUT ANY
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
# details.
#
# You should have received a copy of the GNU General Public License
# along with PISM; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA

"""
Tests for Systems class
We need a way to register machine. Maybe the machine list should be its own repo?
"""

from pathlib import Path

import pytest

from pism_ragis.systems import System


@pytest.fixture(name="machine_file")
def fixture_machine_file():
"""
Return Path to toml file
"""
return Path("tests/data/chinook.toml")


@pytest.fixture(name="machine_dict")
def fixture_machine_dict():
"""
Return system dict
"""
return {
"machine": "chinook",
"MPI": {"mpido": "mpirun -np {cores} -machinefile ./nodes_$SLURM_JOBID"},
"scheduler": {"name": "SLRUM", "submit": "sbatch", "job_id": "SLURM_JOBID"},
"filesystem": {"work_dir": "SLURM_SUBMIT_DIR"},
"queues": {
"t1standard": 24,
"t1small": 24,
"t2standard": 24,
"t2small": 24,
"debug": 24,
"analysis": 24,
},
}


def test_system_from_dict(machine_dict):
"""
Test creating a System from a dictionary
"""
s = System(machine_dict)
assert s.machine == "chinook" # type: ignore[attr-defined] # pylint: disable=E1101


def test_system_from_file(machine_file):
"""
Test creating a System from a toml file
"""
s = System(machine_file)
assert s.machine == "chinook" # type: ignore[attr-defined] # pylint: disable=E1101


# @pytest.fixture
# def systems():
# """
# Return a basic systems
# """

# p = "tests/data"
# return Systems(p)


# def test_systems_len(systems):
# assert len(systems) == 2

0 comments on commit 8cd8626

Please sign in to comment.