Skip to content

Commit

Permalink
Merge branch 'main' into ibatzian/feature/sc-14359/download-mer-plot-…
Browse files Browse the repository at this point in the history
…data-sdk
  • Loading branch information
yias authored Sep 12, 2024
2 parents 422ae1e + 069a602 commit 25703b6
Show file tree
Hide file tree
Showing 17 changed files with 336 additions and 82 deletions.
16 changes: 16 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
Changelog
---------

0.2.3 (2024-08-21)
******************

New:

- Added :py:class:`PostProcessInput<ansys.simai.core.data.model_configuration.PostProcessInput>` class to define post processing input fields.
- Added support for NaN and Inf for Global Coefficients and Post Processings.

Fixes:

- Removed compute argument from :py:meth:`TrainingData.upload_folder()<ansys.simai.core.data.training_data.TrainingData.upload_folder>`
- Fixed Model Configuration to raise a ProcessingError when volume field is missing from a sample specifying volume output.
- Removed wakepy error mode success (deprecated) during optimization.
- Renamed TrainingData method compute() to :py:meth:`TrainingData.extract_data()<ansys.simai.core.data.training_data.TrainingData.extract_data>`.
- Updated documentation of :py:meth:`GeometryDirectory.upload()<ansys.simai.core.data.geometries.GeometryDirectory.upload>`: the ``workspace_id`` argument was moved to ``workspace`` but never updated.

0.2.2 (2024-07-17)
******************

Expand Down
5 changes: 5 additions & 0 deletions doc/source/api_reference/model_configuration.rst
Original file line number Diff line number Diff line change
Expand Up @@ -49,3 +49,8 @@ ModelConfiguration
.. autoclass:: ModelConfiguration()
:members:

PostProcessInput
-----------------

.. autoclass:: PostProcessInput()
:members:
2 changes: 1 addition & 1 deletion doc/source/user_guide.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@ User guide
user_guide/configuration
user_guide/config_file
user_guide/proxy
user_guide/training
user_guide/building_a_model
user_guide/data_exploration
user_guide/best_practices
103 changes: 103 additions & 0 deletions doc/source/user_guide/building_a_model.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
Building a model
================

.. _building_a_model:

.. note::

Building a model with PySimAI is still experimental and subject to API changes.

Rebuilding a model using the last configuration of a project is supported for models created
after v0.1.5 (April 15, 2024).

SimAI allows you to build AI models using past simulation data. This first step to building such models is to upload
your simulation data into a global pool of :class:`training data<ansys.simai.core.data.training_data.TrainingData>` instances.
Then, you assign the imported data to different :class:`Project<ansys.simai.core.data.projects.Project>` instances,
which you will eventually configure in order to build your AI model.

Create a project and upload data
--------------------------------

#. Create a :class:`~ansys.simai.core.client.SimAIClient` instance::

import ansys.simai.core

simai = ansys.simai.core.SimAIClient()

You are prompted for your credentials.

If desired, you can create an instance using a configuration file. For more
information, see :ref:`configuration`.

#. Create a
:class:`TrainingData<ansys.simai.core.data.training_data.TrainingData>` instance
and upload your simulation data into it::

td = simai.training_data.create("my-first-data")
td.upload_folder("/path/to/folder/where/files/are/stored")

#. Create a project::

project = simai.projects.create("my-first-project")

#. Associate the created training data with the created project::

td.add_to_project(project)

Your project is created and your simulation data is associated with it. You can now configure and build your AI model.

Configure and build the model
-----------------------------

#. Import the modules related to model building::

from ansys.simai.core.data.model_configuration import (
DomainOfAnalysis,
ModelConfiguration,
ModelInput,
ModelOutput,
)

#. Set the inputs (:class:`ModelInput<ansys.simai.core.data.model_configuration.ModelInput>`) and outputs (:class:`ModelOutput<ansys.simai.core.data.model_configuration.ModelOutput>`) of the model::

model_input = ModelInput(surface=["wallShearStress"], boundary_conditions=["Vx"])

model_output = ModelOutput(surface=["alpha.water"], volume=["p", "p_rgh"])

#. Set the Global Coefficients::

global_coefficients = [('min("alpha.water")', "minalpha")]

#. Set the Domain of Analysis of the model using the :class:`DomainOfAnalysis<ansys.simai.core.data.model_configuration.DomainOfAnalysis>` instance::

doa = DomainOfAnalysis(
length=("relative_to_min", 15.321, 183.847),
width=("relative_to_min", 1.034, 12.414),
height=("relative_to_min", 2.046, 24.555),
)


#. Configure the model using the :class:`ModelConfiguration<ansys.simai.core.data.model_configuration.ModelConfiguration>` instance::

mdl_conf = ModelConfiguration(
project=project, # project of the model configuration
build_preset="debug", # duration of the build
continuous=False, # continuous training or not
input=model_input, # model input
output=model_output, # model output
global_coefficients=global_coefficients, # Global Coefficients
domain_of_analysis=doa # Domain of Analysis
)

#. Verify if the project meets the requirements for training and launch a build::

if project.is_trainable():
new_model = simai.models.build(mdl_conf)

Your AI model is configured and building.

Learn more
----------

For more information on the actions available to you, see :ref:`training_data`,
:ref:`training_data_parts`, :ref:`projects`, :ref:`model_configuration`, and :ref:`models`
55 changes: 0 additions & 55 deletions doc/source/user_guide/training.rst

This file was deleted.

2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ build-backend = "flit_core.buildapi"

[project]
name = "ansys-simai-core"
version = "0.2.2"
version = "0.2.3"
description = "A python wrapper for Ansys SimAI"
authors = [
{name = "ANSYS, Inc.", email = "[email protected]"},
Expand Down
9 changes: 5 additions & 4 deletions src/ansys/simai/core/data/geometries.py
Original file line number Diff line number Diff line change
Expand Up @@ -491,7 +491,7 @@ def upload( # noqa: D417
For more information, see the :class:`~ansys.simai.core.data.types.NamedFile` class.
metadata: Optional metadata to add to the geometry's simple key-value store.
Lists and nested objects are not supported.
workspace_id: ID or :class:`model <.workspaces.Workspace>` of the workspace to
workspace: ID or :class:`model <.workspaces.Workspace>` of the workspace to
upload the geometry to. This parameter is only necessary if no workspace
is set for the client.
monitor_callback: Optional callback for monitoring the progress of the download.
Expand All @@ -501,15 +501,16 @@ def upload( # noqa: D417
Returns:
Created :py:class:`Geometry` object.
"""
workspace_id = get_id_from_identifiable(workspace, default=self._client._current_workspace)
workspace = self._client.workspaces.get(workspace_id)
workspace = get_object_from_identifiable(
workspace, self._client.workspaces, default=self._client._current_workspace
)
with unpack_named_file(file) as (readable_file, name, extension):
if extension not in workspace.model_manifest.geometry["accepted_file_formats"]:
raise InvalidArguments(
f"Got a file with {extension} extension but expected one of : {workspace.model_manifest.geometry['accepted_file_formats']}"
)
(geometry_fields, upload_id) = self._client._api.create_geometry(
workspace_id, name, extension, metadata
workspace.id, name, extension, metadata
)
geometry = self._model_from(geometry_fields, is_upload_complete=False)
parts = self._client._api.upload_parts(
Expand Down
3 changes: 2 additions & 1 deletion src/ansys/simai/core/data/global_coefficients_requests.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
ComputableDataModel,
Directory,
)
from ansys.simai.core.utils.numerical import cast_values_to_float

if TYPE_CHECKING:
import ansys.simai.core.client
Expand Down Expand Up @@ -179,7 +180,7 @@ def _handle_job_sse_event(self, data):
self._set_is_pending()
elif state == "successful":
logger.debug(f"{self._classname} id {self.id} set status successful")
self._result = data.get("result").get("value")
self._result = cast_values_to_float(data.get("result", {}).get("value"))
self._set_is_over()
elif state in ERROR_STATES:
error_message = f"Computation of global coefficient {target.get('formula')} failed with {data.get('reason', 'UNKNOWN ERROR')}"
Expand Down
43 changes: 34 additions & 9 deletions src/ansys/simai/core/data/model_configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,17 @@ class ModelOutput:
volume: list[str] = None


@dataclass
class PostProcessInput:
"""Designates the variables to use as post-processing input.
Args:
surface: the post-processing input surface variables.
"""

surface: list[str] = None


@dataclass
class ModelConfiguration:
"""Configures the build of a model.
Expand All @@ -209,6 +220,7 @@ class ModelConfiguration:
output: the outputs of the model.
global_coefficients: the Global Coefficients of the model.
domain_of_analysis: The Domain of Analysis of the model configuration.
pp_input: The post-processing input (e.g. a surface variable).
Example:
Define a new configuration and launch a build.
Expand All @@ -222,6 +234,7 @@ class ModelConfiguration:
ModelConfiguration,
ModelInput,
ModelOutput,
PostProcessInput,
)
simai = asc.from_config()
Expand All @@ -237,6 +250,9 @@ class ModelConfiguration:
surface=["Pressure", "WallShearStress_0"], volume=["Velocity_0", "Pressure"]
)
# Define the surface post-processing input
pp_input = PostProcessInput(surface=["Temperature_1"])
# Define the model coefficients
global_coefficients = [("max(Pressure)", "maxpress")]
Expand All @@ -256,6 +272,7 @@ class ModelConfiguration:
output=model_output,
global_coefficients=global_coefficients,
domain_of_analysis=doa,
pp_input=pp_input,
)
# Launch a mode build with the new configuration
Expand All @@ -273,6 +290,7 @@ class ModelConfiguration:
input: ModelInput = field(default_factory=lambda: ModelInput())
output: ModelOutput = field(default_factory=lambda: ModelOutput())
domain_of_analysis: DomainOfAnalysis = field(default_factory=lambda: DomainOfAnalysis())
pp_input: PostProcessInput = field(default_factory=lambda: PostProcessInput())

def __set_gc(self, gcs: list[GlobalCoefficientDefinition]):
verified_gcs = []
Expand Down Expand Up @@ -307,23 +325,23 @@ def __init__(
input: Optional[ModelInput] = None,
output: Optional[ModelOutput] = None,
domain_of_analysis: Optional[DomainOfAnalysis] = None,
surface_pp_input: Optional[list] = None,
pp_input: Optional[PostProcessInput] = None,
):
"""Sets the properties of a build configuration."""
if surface_pp_input is None:
surface_pp_input = []
self.project = project
self.input = ModelInput()
if input is not None:
self.input = input
self.output = ModelOutput()
if output is not None:
self.output = output
self.pp_input = PostProcessInput()
if pp_input is not None:
self.pp_input = pp_input
if boundary_conditions is not None and self.input.boundary_conditions is None:
self.input.boundary_conditions = list(boundary_conditions.keys())
self.build_preset = build_preset
self.continuous = continuous
self.surface_pp_input = surface_pp_input
if fields is not None:
if fields.get("surface_input"):
self.input.surface = [fd.get("name") for fd in fields["surface_input"]]
Expand All @@ -334,7 +352,8 @@ def __init__(
if fields.get("volume"):
self.output.volume = [fd.get("name") for fd in fields["volume"]]

self.surface_pp_input = fields.get("surface_pp_input", [])
if fields.get("surface_pp_input"):
self.pp_input.surface = [fd.get("name") for fd in fields["surface_pp_input"]]

self.domain_of_analysis = domain_of_analysis
if simulation_volume is not None:
Expand Down Expand Up @@ -365,7 +384,7 @@ def _set_doa_axis(self, fld: DomainAxisDefinition, param: str) -> dict[str, Any]
return {"length": fld.length, "type": fld.position, "value": fld.value}

def _to_payload(self):
"""Constracts the payload for a build request."""
"""Constructs the payload for a build request."""

bcs = {}
if self.input.boundary_conditions is not None:
Expand All @@ -390,22 +409,28 @@ def _to_payload(self):
]

volume_fld = []
if self.output.volume is not None:
if self.output.volume:
if not sample_metadata.get("volume"):
raise ProcessingError(
"No volume file was found in the reference sample. A volume file is required to use volume variables."
) from None
volume_fld = [
fd
for fd in sample_metadata.get("volume").get("fields")
for fd in sample_metadata["volume"].get("fields")
if fd.get("name") in self.output.volume
]

gcs = []
if self.global_coefficients is not None:
gcs = [asdict(gc) for gc in self.global_coefficients]

surface_pp_input_fld = self.pp_input.surface or []

flds = {
"surface": surface_fld,
"surface_input": surface_input_fld,
"volume": volume_fld,
"surface_pp_input": self.surface_pp_input if self.surface_pp_input else [],
"surface_pp_input": surface_pp_input_fld,
}

simulation_volume = {
Expand Down
Loading

0 comments on commit 25703b6

Please sign in to comment.