Skip to content

Commit

Permalink
Merge pull request #210 from bessagroup/pr/1.4.4
Browse files Browse the repository at this point in the history
Pr/1.4.4
  • Loading branch information
mpvanderschelling committed Oct 31, 2023
2 parents 322840c + ae54591 commit f95d071
Show file tree
Hide file tree
Showing 14 changed files with 329 additions and 61 deletions.
5 changes: 4 additions & 1 deletion .github/workflows/pr_to_pr.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
name: Pull request to pr/** branches
name: Pull request and push to pr/** branches

on:
pull_request:
branches:
- "pr/**"
push:
branches:
- "pr/**"

jobs:
check-coding-style:
Expand Down
2 changes: 1 addition & 1 deletion VERSION
Original file line number Diff line number Diff line change
@@ -1 +1 @@
1.4.3
1.4.4
4 changes: 2 additions & 2 deletions docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@
project = 'f3dasm'
author = 'Martin van der Schelling'
copyright = '2022, Martin van der Schelling'
version = '1.4.3'
release = '1.4.3'
version = '1.4.4'
release = '1.4.4'


# -- General configuration ----------------------------------------------------
Expand Down
13 changes: 13 additions & 0 deletions docs/source/rst_doc_files/classes/design/experimentsample.rst
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,19 @@ An KeyError will be raised if the key is not found.
>>> experiment_sample.get('param_1')
0.0249
Manually iterating over ExperimentData
----------------------------------------

The :class:`~f3dasm.design.ExperimentData` object can be manually iterated over to get :class:`~f3dasm.design.ExperimentSample` objects for each experiment:

.. code-block:: python
>>> for experiment_sample in experiment_data:
... print(experiment_sample)
ExperimentSample(0 : {'x0': 0.8184054141827567, 'x1': 0.937852542255321, 'x2': 0.7376563782762678} - {})
ExperimentSample(1 : {'x0': 0.7203461491873061, 'x1': 0.7320604457665572, 'x2': 0.2524387342272223} - {})
ExperimentSample(2 : {'x0': 0.35449352388104904, 'x1': 0.11413412225748525, 'x2': 0.1467895592274866} - {})
Storing output parameters to the experiment sample
--------------------------------------------------

Expand Down
2 changes: 1 addition & 1 deletion src/f3dasm/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
# =============================================================================


__version__ = '1.4.3'
__version__ = '1.4.4'


# Log welcome message and the version of f3dasm
Expand Down
83 changes: 76 additions & 7 deletions src/f3dasm/_src/datageneration/datagenerator.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

# Standard
import sys
from abc import abstractmethod
from functools import partial
from typing import Any, Callable

Expand Down Expand Up @@ -43,30 +44,80 @@ class DataGenerator:
"""Base class for a data generator"""

def pre_process(self, experiment_sample: ExperimentSample, **kwargs) -> None:
"""Function that handles the pre-processing"""
"""Interface function that handles the pre-processing of the data generator
Notes
-----
If not implemented the function will be skipped
The experiment_sample is cached inside the data generator. This
allows the user to access the experiment_sample in the pre_process, execute
and post_process functions as a class variable called self.experiment_sample.
"""
...

@abstractmethod
def execute(self, **kwargs) -> None:
"""Function that calls the FEM simulator the pre-processing"""
raise NotImplementedError("No execute function implemented!")
"""Interface function that handles the execution of the data generator
Raises
------
NotImplementedError
If the function is not implemented by the user
Notes
-----
The experiment_sample is cached inside the data generator. This
allows the user to access the experiment_sample in the pre_process, execute
and post_process functions as a class variable called self.experiment_sample.
"""

...

def post_process(self, experiment_sample: ExperimentSample, **kwargs) -> None:
"""Function that handles the post-processing"""
"""Interface function that handles the post-processing of the data generator
Notes
-----
If not implemented the function will be skipped
The experiment_sample is cached inside the data generator. This
allows the user to access the experiment_sample in the pre_process, execute
and post_process functions as a class variable called self.experiment_sample.
"""
...

@time_and_log
def run(self, experiment_sample: ExperimentSample, **kwargs) -> ExperimentSample:
"""Run the data generator
def _run(self, experiment_sample: ExperimentSample, **kwargs) -> ExperimentSample:
"""
Run the data generator
This function chains the following methods together
* pre_process(); to combine the experiment_sample and the parameters
of the data generator to an input file that can be used to run the data generator
* execute(); to run the data generator and generate the response of the experiment
* post_process(); to process the response of the experiment and store it back
in the experiment_sample
The function also caches the experiment_sample in the data generator. This
allows the user to access the experiment_sample in the pre_process, execute
and post_process functions as a class variable called self.experiment_sample.
Parameters
----------
ExperimentSample : ExperimentSample
The design to run the data generator on
kwargs : dict
The keyword arguments to pass to the pre_process, execute and post_process
Returns
-------
ExperimentSample
Processed design
Processed design with the response of the data generator saved in the
experiment_sample
"""
# Cache the design
self.experiment_sample: ExperimentSample = experiment_sample
Expand All @@ -88,7 +139,25 @@ def _post_simulation(self) -> None:
...

def add_pre_process(self, func: Callable, **kwargs):
"""Add a pre-processing function to the data generator
Parameters
----------
func : Callable
The function to add to the pre-processing
kwargs : dict
The keyword arguments to pass to the pre-processing function
"""
self.pre_process = partial(func, **kwargs)

def add_post_process(self, func: Callable, **kwargs):
"""Add a post-processing function to the data generator
Parameters
----------
func : Callable
The function to add to the post-processing
kwargs : dict
The keyword arguments to pass to the post-processing function
"""
self.post_process = partial(func, **kwargs)
2 changes: 1 addition & 1 deletion src/f3dasm/_src/datageneration/functions/function.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def execute(self, experiment_sample: ExperimentSample) -> ExperimentSample:
experiment_sample["y"] = self(x).ravel().astype(np.float32)
return experiment_sample

def run(self, experiment_sample: ExperimentSample, **kwargs) -> ExperimentSample:
def _run(self, experiment_sample: ExperimentSample, **kwargs) -> ExperimentSample:
return self.execute(experiment_sample)

def _retrieve_original_input(self, x: np.ndarray):
Expand Down
52 changes: 50 additions & 2 deletions src/f3dasm/_src/design/domain.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@
from __future__ import annotations

# Standard
import math
import pickle
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Dict, Iterator, List, Sequence, Type
from typing import Any, Dict, Iterable, Iterator, List, Sequence, Type

# Third-party core
import numpy as np
Expand Down Expand Up @@ -252,7 +253,14 @@ def add_int(self, name: str, low: int, high: int, step: int = 1):
>>> domain.add_int('param1', 0, 10, 2)
>>> domain.space
{'param1': DiscreteParameter(lower_bound=0, upper_bound=10, step=2)}
Note
----
If the lower and upper bound are equal, then a constant parameter
will be added to the domain!
"""
if low == high:
self.add_constant(name, low)
self._add(name, DiscreteParameter(low, high, step))

def add_float(self, name: str, low: float, high: float, log: bool = False):
Expand All @@ -275,8 +283,16 @@ def add_float(self, name: str, low: float, high: float, log: bool = False):
>>> domain.add_float('param1', 0., 10., log=True)
>>> domain.space
{'param1': ContinuousParameter(lower_bound=0., upper_bound=10., log=True)}
Note
----
If the lower and upper bound are equal, then a constant parameter
will be added to the domain!
"""
self._add(name, ContinuousParameter(low, high, log))
if math.isclose(low, high):
self.add_constant(name, low)
else:
self._add(name, ContinuousParameter(low, high, log))

def add_category(self, name: str, categories: Sequence[CategoricalType]):
"""Add a new categorical input parameter to the domain.
Expand Down Expand Up @@ -573,6 +589,38 @@ def _filter(self, type: Type[Parameter]) -> Domain:
if isinstance(parameter, type)}
)

def select(self, names: str | Iterable[str]) -> Domain:
"""Select a subset of parameters from the domain.
Parameters
----------
names : str or Iterable[str]
The names of the parameters to select.
Returns
-------
Domain
A new domain with the selected parameters.
Example
-------
>>> domain = Domain()
>>> domain.space = {
... 'param1': ContinuousParameter(lower_bound=0., upper_bound=1.),
... 'param2': DiscreteParameter(lower_bound=0, upper_bound=8),
... 'param3': CategoricalParameter(categories=['cat1', 'cat2'])
... }
>>> domain.select(['param1', 'param3'])
Domain({'param1': ContinuousParameter(lower_bound=0, upper_bound=1),
'param3': CategoricalParameter(categories=['cat1', 'cat2'])})
"""

if isinstance(names, str):
names = [names]

return Domain(space={key: self.space[key] for key in names})

# Miscellaneous
# =============================================================================

Expand Down
17 changes: 17 additions & 0 deletions src/f3dasm/_src/experimentdata/_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,23 @@ def n_best_samples(self, nosamples: int, column_name: List[str] | str) -> pd.Dat
"""
return self.data.nsmallest(n=nosamples, columns=column_name)

def select_columns(self, columns: Iterable[str] | str) -> _Data:
"""Filter the data on the selected columns.
Parameters
----------
columns : Iterable[str] | str
The columns to select.
Returns
-------
_Data
The data only with the selected columns
"""
# This is necessary otherwise self.data[columns] will be a Series
if isinstance(columns, str):
columns = [columns]
return _Data(self.data[columns])
# Append and remove data
# =============================================================================

Expand Down
Loading

0 comments on commit f95d071

Please sign in to comment.