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

Common notation #146

Merged
merged 4 commits into from
Nov 9, 2023
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
4 changes: 4 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
Changelog
=========

0.3.0 (2023-09-14)
------------------
- Add common notation see https://github.com/hpsim/OBR/pull/146

0.2.0 (2023-09-14)
------------------
- Add --json=file.json option to obr query, which writes the result of the query to a json file.
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "obr"
version = "0.2.0"
version = "0.3.0"

dependencies = [
"click",
Expand Down
57 changes: 43 additions & 14 deletions src/obr/create_tree.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,17 +38,40 @@ def flatten(d, parent_key="", sep="/"):
return dict(items)


def get_path_from(operation: dict, value) -> str:
def get_path_from(operation: dict, value: dict) -> str:
"""Derive a view path from schema and the value dict

Returns: a view path as string
"""
if not operation.get("schema"):
logging.error("Error Schema missing for Set schema to allow creating views")
raise KeyError

return operation["schema"].format(**flatten(value)) + "/"


def extract_from_operation(operation, value):
"""takes an operation"""
def extract_from_operation(operation: dict, value) -> dict:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What type could value be in this context?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the current design it is either are number, string or dict. This should be refactored at some point such that it is only a number or string

"""takes an operation dictionary and do some processing
It extracts keys path and args from the operation dictionary
based on given value. The passed value is used as a selector
to create keys path and args.

- args are later used to pass it to the selected operation,
either the operation contains:
1. {key: key, values: [v1, v2, ...]} or
2. {values: [{k:v1},{k:v2}] }
2. {common: {c1:v1, c2:v2}, values: [{k:v1},{k:v2}] }

- the path is derived from the schema key value pair
a entry {schema: path/{foo}, values: [{foo: 1}]} will be formatted
to path/1

Returns: a dictionary with keys, path and args
"""
key = operation.get("key", "").replace(".", "_dot_")
common = operation.get("common", {})
if isinstance(value, dict) and common:
value.update(common)
if not key:
path = get_path_from(operation, value)
args = value
Expand All @@ -63,7 +86,7 @@ def extract_from_operation(operation, value):
path = "{}/{}/".format(key_, value)
keys = [key]

return keys, path, args
return {"keys": keys, "path": path, "args": args}


def generate_view(
Expand Down Expand Up @@ -141,8 +164,12 @@ def add_variations(
variation: list,
parent_job: Job,
id_path_mapping: dict,
) -> list:
"""Recursively adds variations to the project"""
) -> list[str]:
"""Recursively adds variations to the project and initialises the jobs. This
creates the workspace/uid folder and signac files as sideeffect.

Returns: A list of all operation names
"""
for operation in variation:
sub_variation = operation.get("variation", {})

Expand All @@ -155,27 +182,29 @@ def add_variations(
continue

# derive path name from schema or key value
keys, path, args = extract_from_operation(operation, value)
path = clean_path(path)
parse_res = extract_from_operation(operation, value)
path = clean_path(parse_res["path"])
base_dict = deepcopy(to_dict(parent_job.sp))

statepoint = {
"keys": keys,
"keys": parse_res["keys"],
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is keys always existing in parse_keys ? Or could this potentially throw a KeyError?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This has always keys, since extract_from_operation always adds them.

"parent_id": parent_job.id,
"operation": operation["operation"],
"has_child": True if sub_variation else False,
"pre_build": operation.get("pre_build", []),
"post_build": operation.get("post_build", []),
**args,
**parse_res["args"],
}
statepoint["parent"] = base_dict

job = project.open_job(statepoint)
setup_job_doc(job, value)
setup_job_doc(job)
job.init()
job.doc["state"]["global"] = ""

id_path_mapping[job.id] = id_path_mapping.get(parent_job.id, "") + path
id_path_mapping[job.id] = (
id_path_mapping.get(parent_job.id, "") + parse_res["path"]
)

if sub_variation:
operations = add_variations(
Expand All @@ -186,7 +215,7 @@ def add_variations(
return operations


def setup_job_doc(job, value, reset=False):
def setup_job_doc(job: Job, reset: bool =False) -> None:
"""Sets basic information in the job document"""

# dont overwrite old job state on init so that we can update a tree without
Expand Down Expand Up @@ -221,7 +250,7 @@ def create_tree(
base_case_state.update({k: v for k, v in config["case"].items()})
of_case = project.open_job(base_case_state)

setup_job_doc(of_case, value=[])
setup_job_doc(of_case)
of_case.init()

operations: list = []
Expand Down
58 changes: 57 additions & 1 deletion tests/test_create_tree.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from obr.create_tree import create_tree
from obr.create_tree import create_tree, add_variations, extract_from_operation
from obr.signac_wrapper.operations import OpenFOAMProject

import pytest
Expand Down Expand Up @@ -33,6 +33,62 @@ def emit_test_config():
}


def test_extract_from_operation():
operation = {"operation": "op1", "key": "foo", "values": [1, 2, 3]}
res = extract_from_operation(operation, 1)
assert res == {"keys": ["foo"], "path": "foo/1/", "args": {"foo": 1}}

operation = {
"operation": "op1",
"schema": "path/{foo}",
"values": [{"foo": 1}, {"foo": 2}, {"foo": 3}],
}
res = extract_from_operation(operation, {"foo": 1})
assert res == {"keys": ["foo"], "path": "path/1/", "args": {"foo": 1}}

operation = {
"operation": "op1",
"schema": "path/{foo}",
"common": {"c1": "v1"},
"values": [{"foo": 1}, {"foo": 2}, {"foo": 3}],
}
res = extract_from_operation(operation, {"foo": 1})
assert res == {
"keys": ["foo", "c1"],
"path": "path/1/",
"args": {"foo": 1, "c1": "v1"},
}


def test_add_variations():
class MockJob:
id = "0"
sp = {}
doc = {}

def init(self):
pass

class MockProject:
def open_job(self, statepoint):
return MockJob()

operations = []
test_variation = [
{
"operation": "n/a",
"schema": "n/a",
"values": [{"foo": 1}, {"foo": 2}, {"foo": 3}],
}
]
id_path_mapping = {}
operations = add_variations(
operations, MockProject(), test_variation, MockJob(), id_path_mapping
)

assert operations == ["n/a"]


def test_create_tree(tmpdir, emit_test_config):
project = OpenFOAMProject.init_project(root=tmpdir)

Expand Down
Loading