.. _python_API_cloud_asset_introduction:

.. currentmodule:: flow360

Cloud assets
============

Cloud assets refer to geometry, mesh and cases that have been uploaded to/generated in the cloud.
User can use the IDs of existing cloud assets to reference them and interact with them.

Referencing existing cloud assets
------------------------------------

.. code-block:: python
    
    existing_cloud_project = fl.Project.from_cloud("prj-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
    existing_cloud_geometry = fl.Geometry.from_cloud("geo-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
    existing_cloud_volume_mesh = fl.VolumeMesh.from_cloud("vm-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")
    existing_cloud_case = fl.Case.from_cloud("case-xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx")

.. _accessing_entities:

Accessing the entities within cloud assets
------------------------------------------

For Geometry, VolumeMesh assets, user can access the entities within them by using the entity name. A full list of entities contained within each type of cloud asset is shown below:

:code:`Geometry`

- Grouped faces
- Grouped edges

:code:`VolumeMesh`

- Boundaries
- Volume zones

For example when specifying the :class:`Wall` boundary condition, user can access the boundaries in the above :code:`existing_cloud_volume_mesh` by:

.. code-block:: python
    
    wall = fl.Wall(
        name="Wall",
        entities=[
            existing_cloud_volume_mesh["leftWing"],
            existing_cloud_volume_mesh["rightWing"],
        ],
    )

which will select both the :code:`leftWing` and :code:`rightWing` boundaries in the volume mesh.

.. note::
    Before selecting entities in :code:`Geometry`, user needs to specify grouping tags for faces and edges. See :ref:`below<geometry_asset_introduction>` for more information.

We also support naming pattern with wildcard/regular expression for selecting entities. For example, let's say the :code:`existing_cloud_volume_mesh` has the following boundaries and zones:

Boundaries:

- :code:`leftWing`
- :code:`rightWing`
- :code:`fuselage`
- :code:`farfield`

Volume zones:

- :code:`ZoneWithWing`
- :code:`Farfield`

User can achieve the same operation as above by:

.. code-block:: python
    
    wall = fl.Wall(
        name="Wall",
        entities=[
            existing_cloud_volume_mesh["*Wing"],
        ],
    )

which will use wildcard to automatically match all the entities with that naming pattern. This results in the following entities being selected:

Boundaries:

- :code:`leftWing`
- :code:`rightWing`

Volume zones:

- :code:`ZoneWithWing`

However since the :py:attr:`Wall.entities` in :class:`Wall` knows it only accepts surface-type entities. The :code:`ZoneWithWing` will be automatically filtered out, resulting in only the :code:`leftWing` and :code:`rightWing` being selected.
Therefore if the user wants :class:`SurfaceOutput` for all the boundaries, the user can do:

.. code-block:: python
    
    wall = fl.SurfaceOutput(
        name="OutputOnAllBCs",
        entities=[
            existing_cloud_volume_mesh["*"],
        ],
        output_fields=["Cp"],
    )

which will dump outputs on all the boundaries (:code:`leftWing`, :code:`rightWing`, :code:`fuselage`, :code:`farfield`).

.. _project_introduction:

Project
-------

Project serves as the container of all generated cloud assets (like the surface meshes, volume meshes and cases) that share the same root asset.
It also is the interface for user to generate new cloud assets. For example forking an existing case to create a new case.
An illustration of the content of project (also known as the "Project Tree") is shown below:

.. mermaid::
   :align: center

    graph TD
        subgraph Project["Project"]
            Geometry["Geometry"]
            SurfaceMesh1["SurfaceMesh 1"]
            SurfaceMesh2["SurfaceMesh 2"]
            VolumeMesh1["VolumeMesh 1"]
            VolumeMesh2["VolumeMesh 2"]
            Case1["Case 1"]
            Case2["Case 2"]
            Case3["Case 3"]

            Geometry --> SurfaceMesh1
            Geometry --> SurfaceMesh2
            SurfaceMesh1 --> VolumeMesh1
            SurfaceMesh1 --> VolumeMesh2
            VolumeMesh1 --> Case1
            VolumeMesh2 --> Case2
            Case2 --> Case3
        end

Create a project
~~~~~~~~~~~~~~~~

Project can be created by uploading a geometry or a volume mesh which will become the root asset of the project.
Currently we do not support creating project from a surface mesh file.

.. mermaid::
   :align: center

   graph TD
       Geometry -->|upload and creates| Project
       VolumeMesh -->|upload and creates| Project

To create a project from a geometry or a volume mesh with PythonAPI:

.. code-block:: python
    
    project_from_geometry = fl.Project.from_file(
        "my_geometry.csm", name="Project from geometry"
    )
    project_from_geometry = fl.Project.from_file(
        "my_volume_mesh.cgns", name="Project from volume mesh"
    )

Creating new cloud assets from a project
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Once the project is created user can use the project and creates new cloud assets via the project.
For example, if I want to run a case from `"my_geometry.csm"`, I can do the following:

.. _example_submit_case:
.. code-block:: python
    
    project_from_geometry.run_case(my_params, name="my new case")

where :code:`my_params` is a :class:`SimulationParams` instance. This new case will be associated with the project.

The new asset will be then appended to the project tree.
The node to which the new asset is appended is completely determined by the user setting.
We will analyze the setting and try to browse through the project assets to see if any asset has the exact same meshing or case setting.
If such asset is found the new asset will be appended to that node or the asset corresponding to that node will be returned directly and no meshing/case will be run.

For example, in the :ref:`above<example_submit_case>` example, let's say :code:`VolumeMesh 2` has the same meshing 
setting as :code:`my_params`. Then the new case will be appended to the :code:`VolumeMesh 2` and only 
the mesh generation will be skipped.

.. mermaid::
   
    graph TD
        classDef grayNode fill:#ddd,stroke:#bbb,stroke-width:2px;
        classDef grayText color:#bbb,fill-opacity:0.3;
        classDef highlightText color:#ff5436,fill-opacity:0.9;

        subgraph Project["Project"]
            Geometry["Geometry"]
            SurfaceMesh1["SurfaceMesh 1"]
            SurfaceMesh2["SurfaceMesh 2"]
            VolumeMesh1["VolumeMesh 1"]
            VolumeMesh2["VolumeMesh 2"]
            Case1["Case 1"]
            Case2["Case 2"]
            Case3["Case 3"]
            Case4["**my new case**"]

            Geometry --> SurfaceMesh1
            Geometry --> SurfaceMesh2
            SurfaceMesh1 --> VolumeMesh1
            SurfaceMesh1 --> VolumeMesh2
            VolumeMesh1 --> Case1
            VolumeMesh1 --> Case4
            VolumeMesh2 --> Case2
            Case2 --> Case3
        end

        %% These are irrelevant nodes
        class SurfaceMesh2,VolumeMesh2,Case1,Case2,Case3 grayNode;
        class SurfaceMesh2,VolumeMesh2,Case1,Case2,Case3 grayText;
        %% These are highlighted nodes
        class Case4 highlightText;

Another scenario is that if the user happens to have a case with the **exact** same setting (meshing and case)
as :code:`my_params` in the project, there will be no new case created and the existing case will be returned.
For example if :code:`my_params` is exactly the same as the one that created :code:`Case 2`, 
then :code:`Case 2` will be returned.

.. mermaid::
   
    graph TD
        classDef grayNode fill:#ddd,stroke:#bbb,stroke-width:2px;
        classDef grayText color:#bbb,fill-opacity:0.3;
        classDef highlightText color:#ff5436,fill-opacity:0.9;

        subgraph Project["Project"]
            Geometry["Geometry"]
            SurfaceMesh1["SurfaceMesh 1"]
            SurfaceMesh2["SurfaceMesh 2"]
            VolumeMesh1["VolumeMesh 1"]
            VolumeMesh2["VolumeMesh 2"]
            Case1["Case 1"]
            Case2["**Case 2**"]
            Case3["Case 3"]

            Geometry --> SurfaceMesh1
            Geometry --> SurfaceMesh2
            SurfaceMesh1 --> VolumeMesh1
            SurfaceMesh1 --> VolumeMesh2
            VolumeMesh1 --> Case1
            VolumeMesh2 --> Case2
            Case2 --> Case3
        end

        %% These are irrelevant nodes
        class SurfaceMesh2,VolumeMesh1,Case1,Case3 grayNode;
        class SurfaceMesh2,VolumeMesh1,Case1,Case3 grayText;
        %% These are highlighted nodes
        class Case2 highlightText;


.. _geometry_asset_introduction:

Geometry
--------

User can use :code:`Geometry` asset to specify the grouping of faces and edges and these grouping information will be
used when generating the mesh and running the case.
Note that only the grouping/tagging information stored in the geometry file can be used.
Currently we do not support creating new groups/tags.

For example in the :ref:`quick start example<quick_start_example_code>`, we tried to access the available groupings in the geometry:

.. code-block:: python
    
    geo.show_available_groupings(verbose_mode=True)

and we can see output:

.. code-block:: text

    INFO:  >> Available attribute tags for grouping **faces**:                                                                                      
    INFO:     >> Tag 0: groupName. Grouping with this tag results in:                                                                               
    INFO:         >> Group 0: leftWing                                                                                                `              
    INFO:            IDs: ['body0001_face0001', 'body0001_face0002', 'body0001_face0003', 'body0001_face0004']                                      
    INFO:         >> Group 1: fuselage                                                                                                              
    INFO:            IDs: ['body0001_face0005', 'body0001_face0006', 'body0001_face0007', 'body0001_face0008', 'body0001_face0009', 'body0001_face0010']                                                                                                                                 
    INFO:         >> Group 2: rightWing                                                                                                             
    INFO:            IDs: ['body0001_face0011', 'body0001_face0012', 'body0001_face0013', 'body0001_face0014']                                      
    INFO:  >> Available attribute tags for grouping **edges**:                                                                                      
    INFO:     >> Tag 0: edgeName. Grouping with this tag results in:                                                                                
    INFO:         >> Group 0: trailingEdge                                                                                                          
    INFO:            IDs: ['body0001_edge0001', 'body0001_edge0005', 'body0001_edge0026', 'body0001_edge0030']                                      
    INFO:         >> Group 1: leadingEdge                                                                                                           
    INFO:            IDs: ['body0001_edge0007', 'body0001_edge0032']                                                                                
    INFO:         >> Group 2: body0001_edge0002                                                                                                     
    INFO:            IDs: ['body0001_edge0002']                                                                                                     
    INFO:         >> Group 3: body0001_edge0004                                                                                                     
    INFO:            IDs: ['body0001_edge0004']                                                                                                     
    INFO:         >> Group 4: body0001_edge0006                                                                                                     
    INFO:            IDs: ['body0001_edge0006']                                                                                                     
    INFO:         >> Group 5: body0001_edge0008                                                                                                     
    INFO:            IDs: ['body0001_edge0008']                                                                                                     
    INFO:         >> Group 6: body0001_edge0009                                                                                                     
    ...

Note that the above geometry file only specifies tag for some of the edges
(:code:`"body0001_edge0001"`, :code:`"body0001_edge0005"`, :code:`"body0001_edge0026"`, :code:`"body0001_edge0030"`, :code:`"body0001_edge0007"`, :code:`"body0001_edge0032"`)
, all the other edges does not have the tag "edgeName". If "edgeName" is specified as the edge tag, the edges without this tag will be grouped by themselves (one edge per group).
In other words when user do:

.. code-block:: python
    
    geo.group_edges_by_tag("edgeName")

The resulting grouped edges will be:

- :code:`trailingEdge`
- :code:`leadingEdge`
- :code:`body0001_edge0002`
- :code:`body0001_edge0004`
- :code:`body0001_edge0006`...

For face grouping user can call:

.. code-block:: python
    
    geo.group_faces_by_tag("groupName")

User can always use "faceId" and "edgeId" to group faces and edges respectively. This results in each individual face/edge being grouped.