Metadata-Version: 2.4
Name: flexcompute-flow-report
Version: 0.1.0
Summary: Python client for Flexcompute Flow interactive reports
Project-URL: Documentation, https://docs.flexcompute.com/projects/flow360/en/latest/
Author-email: Flexcompute <support@flexcompute.com>
Maintainer-email: Flexcompute <support@flexcompute.com>
License-Expression: LGPL-2.1-only
License-File: LICENSE
Keywords: CFD,interactive reports
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Science/Research
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3 :: Only
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering
Requires-Python: <3.14,>=3.10
Requires-Dist: flow360<26,>=25.10.4
Provides-Extra: dev
Requires-Dist: build<2,>=1.2; extra == 'dev'
Requires-Dist: pytest<8,>=7.1; extra == 'dev'
Requires-Dist: ruff<1,>=0.12; extra == 'dev'
Requires-Dist: twine<7,>=6; extra == 'dev'
Description-Content-Type: text/markdown

# Flexcompute Flow Report

`flexcompute-flow-report` is a standalone Python client for creating, loading,
updating, and reusing interactive Flexcompute Flow reports. It is separate from
both the Flow360 Python client and the existing PDF report package named
`flow360-report`.

This README describes the public API as implemented. Parameter names and
accepted values in this document are authoritative; the client does not infer
chart variables from display labels or from CSV column names.

## AI agent bootstrap

Installing the wheel makes its documentation available, but it does not cause
an AI agent to read that documentation. An agent runner should load the packaged
guide and add it to the model's system or developer instructions before sending
the user's task:

```python
from flexcompute_flow_report import get_agent_guide

agent_instructions = get_agent_guide()
# Inject agent_instructions before the user task is submitted to the model.
```

Shell-based runners can obtain the same text with:

```bash
flexcompute-flow-report-guide
```

The guide is deliberately shorter than this API reference. It defines the
mandatory capability-discovery, exact-ID, compatibility-check, Scene-reuse, and
chart-selection rules for AI-generated Report workflows. Package code cannot
force a generic external agent to follow these rules; the runner must perform
the injection step.

Compatibility checks are diagnostic rather than a one-shot gate for the whole
task. An error blocks the current candidate setup from being submitted
unchanged; the agent should use resource capabilities to adapt and recheck it.
Only an explicit exact-equivalence requirement makes every warning blocking.

## Installation and environment

Install the released package from PyPI:

```bash
python -m pip install flexcompute-flow-report
```

Maintainers testing an unreleased build can instead download the distribution
artifact produced by the `Flexcompute Flow Report` GitHub Actions
workflow and install its wheel directly.

Installing the wheel also installs a compatible Flow360 Python client and
Pydantic version.

The client uses the active Flow360 environment. That environment must expose
the interactive Report API at `/v2/reports`. Reading reusable visualization
Scenes also requires `GET /v2/scenes/{sceneId}`. A 404 such as
`No static resource v2/reports` means that this endpoint is not deployed in the
active environment. It does not mean that the report configuration is invalid.
The Flow360 web UI can use a different report route, so the presence of
UI-created reports does not prove that `/v2/reports` is available.

`get_capabilities(resource)` reads that resource's generated
`visualize/manifest/manifest.json`. Applying a Scene viewpoint or a
`Report.Camera` with an explicit `unit` as a per-resource camera also reads
`GET /v2/{resource-path}/{resourceId}/simulation/file` so physical camera
lengths can be converted to the target resource's model units.

## Minimal example

```python
import flow360 as fl
from flexcompute_flow_report import Report

baseline = fl.Case(id="case-11111111-1111-1111-1111-111111111111")
variant = fl.Case(id="case-22222222-2222-2222-2222-222222222222")

report = Report.create(
    name="Wing comparison",
    resources=[baseline, variant],
    reference=baseline,
    sections=[
        Report.Summary(resource_type="case"),
        Report.Visualization(),
        Report.Chart(y=["CL", "CD"], comparison="delta"),
    ],
)

print(report.id)
print(report.web_url)
```

Report inputs should normally be lightweight Flow360 resource references. All
four supported resource classes can be constructed directly from their cloud
IDs:

```python
resources = [
    fl.Case(id="case-11111111-1111-1111-1111-111111111111"),
    fl.Geometry(id="geo-11111111-1111-1111-1111-111111111111"),
    fl.SurfaceMesh(id="sm-11111111-1111-1111-1111-111111111111"),
    fl.VolumeMesh(id="vm-11111111-1111-1111-1111-111111111111"),
]
```

The client obtains the cloud data needed by each operation through these
lightweight references. Report compilation reads resource metadata,
`get_capabilities(resource)` reads the generated visualization Manifest, and
raw SimulationJSON is read only when `Report.create(...)` or `report.update(...)`
compiles a per-resource Scene viewpoint or `Report.Camera` with an explicit
`unit`. The client does not need Geometry, SurfaceMesh, or VolumeMesh entity
registries, so calling their `from_cloud()` methods first is unnecessary and can
fail when a historical resource no longer passes the current Flow360 entity
schema. Use `from_cloud()` only when other Flow360 operations require a fully
deserialized resource.

`ReportResourceInput` is exported from `flexcompute_flow_report` for callers that
want to annotate reusable resource collections.

`Report.Summary` always requires `resource_type`. The four summary sections
exist in every report; adding `Report.Summary(...)` customizes the summary for
the specified resource type.

## Guides

Detailed task-oriented guides are packaged with the client:

- [AI agent guide](docs/agent-guide.md): mandatory preflight and selection rules
  for AI-generated Report workflows.
- [Reuse saved Scenes](docs/scenes.md): load a Workbench Scene, validate exact
  compatibility, and reuse its visualization setting or viewpoint.
- [Configure visualizations across resources](docs/visualization-capabilities.md):
  discover Manifest-backed capabilities, interpret output and target names,
  validate setups, and construct per-resource views.
- [Configure residual and CFL charts](docs/charts.md): select metadata-defined
  series groups and compatible X axes.

An installed wheel keeps this README at `flexcompute_flow_report/README.md` and
the guides under `flexcompute_flow_report/docs/`, so they remain available to
local AI and automation tools without access to the source repository.

AI and automation workflows should inject the AI agent guide first, then use the
topic guides for task-specific details.

## Report lifecycle

### Create

```python
report = Report.create(
    name="Wing comparison",
    resources=[baseline, variant],
    sections=[Report.Visualization(), residual_chart],
)
```

`Report.create(...)` parameters:

| Parameter | Type | Default | Meaning |
| --- | --- | --- | --- |
| `name` | `str` | required | Non-empty report name. |
| `resources` | iterable of `ReportResourceInput` | required | Lightweight `Case`, `Geometry`, `SurfaceMesh`, or `VolumeMesh` references. At least one and at most 50. |
| `sections` | iterable of section objects or `None` | `None` | Additional/customized sections. The four default summary sections are always present. |
| `reference` | cloud resource, resource ID, or `None` | `None` | Reference Case. If omitted, the first Case is used. |
| `aliases` | mapping of resource ID to display name or `None` | `None` | Report-only resource display names. |
| `description` | `str` | `""` | Report description. |
| `tags` | iterable of `str` or `None` | `None` | Tags are trimmed and deduplicated in input order. |
| `parent_folder_id` | `str` | Flow360 root folder | Destination folder ID. |

### Load

```python
report = Report.from_cloud("report-id")
```

`Report.from_cloud(report_id)` loads report metadata and `configJson` from the
active Flow360 environment. The returned object remains associated with that
environment even if the global environment changes later.

### Update the same report

Metadata-only updates send only the supplied fields:

```python
report.update(
    name="Renamed comparison",
    description="Release comparison after review",
    tags=["release", "reviewed"],
)
```

To replace the report configuration, provide both `resources` and `sections`:

```python
report.update(
    resources=[baseline, variant],
    reference=baseline,
    sections=[
        Report.Summary(resource_type="case"),
        Report.Visualization(),
        residual_chart,
    ],
)
```

Configuration updates rebuild the complete SDK-managed configuration from the
provided resources and sections. They are not a patch of individual fields in
the existing `configJson`.

`Report.update(...)` parameters:

| Parameter | Type | Default | Meaning |
| --- | --- | --- | --- |
| `resources` | iterable of cloud resources or `None` | `None` | New complete resource set. Must be supplied together with `sections`. |
| `sections` | iterable of section objects or `None` | `None` | New complete section specification. Must be supplied together with `resources`. |
| `reference` | cloud resource, resource ID, or `None` | `None` | Reference Case for the rebuilt configuration. Requires `resources` and `sections`. |
| `aliases` | mapping or `None` | `None` | Aliases for the rebuilt configuration. Requires `resources` and `sections`. |
| `name` | `str` or `None` | `None` | New name; `None` leaves it unchanged. |
| `description` | `str` or `None` | `None` | New description; `None` leaves it unchanged and `""` clears it. |
| `tags` | iterable of `str` or `None` | `None` | New complete tag list; `None` leaves it unchanged and `[]` clears it. |
| `parent_folder_id` | `str` or `None` | `None` | New folder ID; `None` leaves it unchanged. |

`update()` mutates and returns the same `Report` object. Its report ID and
`web_url` do not change.

### Refresh a long-lived object

```python
report.refresh()
print(report.status)
```

`refresh()` reloads metadata and `configJson` into the existing object. Use it
when the report may have changed through the web UI, another process, or an
asynchronous status transition. It is not required after a successful
`update()`.

This also provides a supported path for reading back a camera tuned and saved
in the web UI:

```python
report.refresh()
visualization = next(
    section for section in report.config.sections if section.type == "visualization"
)
persisted = visualization.config.sync.camera.global_params
camera = None if persisted is None else Report.Camera(**persisted.model_dump())
```

The persisted camera is already expressed in the Report UI's canonical length
unit, so the reconstructed `Report.Camera` intentionally omits `unit`. It can
be passed to a later `Report.Visualization(camera=camera)`.

### Apply an existing report to new resources

```python
source = Report.from_cloud("source-report-id")
copied = source.apply_to_new(
    name="New release comparison",
    resources=[new_baseline, new_variant],
    reference=new_baseline,
)
```

`Report.apply_to_new(...)` parameters:

| Parameter | Type | Default | Meaning |
| --- | --- | --- | --- |
| `name` | `str` | required | Name of the new report. |
| `resources` | iterable of cloud resources | required | Resource pool for the copied configuration. |
| `reference` | cloud resource, resource ID, or `None` | `None` | Reference Case in the new resource pool. |
| `aliases` | mapping or `None` | `None` | Display names for the new resources. |
| `description` | `str` | `""` | Description of the new report. |
| `tags` | iterable of `str` or `None` | `None` | Tags for the new report. |
| `parent_folder_id` | `str` or `None` | `None` | Destination folder. `None` reuses the source report folder. |

The method preserves section IDs, layout, and display settings. It remaps
visualization and chart resource selections to the new resources.
Visualization sections silently use the first eight selected resources whose
type matches the section, in the order supplied to `resources`. Chart sections
use the first 10 selected Cases; if the reference Case is outside those 10, it
replaces the tenth Case. These are the web UI's apply-to-new rules. A
resource-specific Case force scope is reset to `total` because old face or
body-group IDs cannot be safely reused.

### Public report properties

| Property | Type | Meaning |
| --- | --- | --- |
| `id` | `str` | Report ID. |
| `project_id` | `str \| None` | Project shell associated with the report. |
| `name` | `str` | Report name. |
| `description` | `str \| None` | Report description. |
| `tags` | `list[str]` | Copy of report tags. |
| `parent_folder_id` | `str \| None` | Parent folder ID. |
| `workspace_id` | `str \| None` | Workspace ID. |
| `status` | `str` | Current report status returned by the API. |
| `viewed` | `bool \| None` | Viewed state returned by the API. |
| `is_deleted` | `bool \| None` | Deleted state returned by the API. |
| `associated_resources` | list | Copy of associated resource metadata. |
| `config` | `ReportConfig` | Deep copy of the loaded or submitted configuration. Mutating it does not update the cloud report. |
| `config_json` | `str` | Serialized form of `config`. |
| `created_at` | `datetime \| None` | Creation timestamp. |
| `updated_at` | `datetime \| None` | Last update timestamp. |
| `web_url` | `str` | URL in the environment from which the object was created or loaded. |

## Section API

### `Report.Summary`

Customizes one of the four built-in summary sections.

| Parameter | Type | Default | Meaning |
| --- | --- | --- | --- |
| `resource_type` | `"case"`, `"geometry"`, `"surface_mesh"`, or `"volume_mesh"` | required | Summary section to customize. |
| `title` | `str \| None` | `None` | Custom title; `None` uses the standard title. |
| `fields` | `list[str] \| None` | `None` | Visible fields. For Case, `None` uses the standard subset; for other types, `None` shows all supported fields. |
| `force_scope` | `"total"`, `"faces"`, `"body_groups"`, or `None` | `None` | Case-summary force aggregation. Only valid for `resource_type="case"`. |
| `force_scope_ids` | `list[str] \| None` | `None` | Required for `faces` or `body_groups`; unavailable for `total`. |

Discover valid fields:

```python
Report.Summary.supported_fields("case")
Report.Summary.supported_fields("geometry")
Report.Summary.supported_fields("surface_mesh")
Report.Summary.supported_fields("volume_mesh")
```

Example:

```python
Report.Summary(
    resource_type="case",
    title="Run summary",
    fields=["velocity", "alpha", "beta", "cl", "cd"],
    force_scope="faces",
    force_scope_ids=["wing"],
)
```

### `Report.ChartVariable`

Represents a Y-axis variable that is defined by result metadata rather than by
the chart's fixed force-variable list.

| Constructor parameter | Type | Default | Meaning |
| --- | --- | --- | --- |
| `source` | `"csv"` or `"udd"` | `"csv"` | Result metadata source. |
| `file_name` | `str` | required | Result file name. |
| `column` | `str` | required | Metadata Y-axis column. Use a factory instead of the internal series-group sentinel. |

Prefer the following factories over calling the constructor directly:

| Factory | Meaning |
| --- | --- |
| `Report.ChartVariable.csv(file_name, column)` | CSV metadata exposes `column` as a Y-axis variable. |
| `Report.ChartVariable.udd(file_name, column)` | UDD metadata exposes `column` as a Y-axis variable. |
| `Report.ChartVariable.csv_series(file_name)` | CSV metadata exposes one Y-axis series group; select members with `Report.Chart(series=[...])`. |
| `Report.ChartVariable.udd_series(file_name)` | UDD metadata exposes one Y-axis series group; select members with `Report.Chart(series=[...])`. |

Leading `/` and `results/` are removed from `file_name`. Do not pass
`"__series__"` directly; use a series-group factory. The factory creates that
internal sentinel because it is part of the Report UI's persisted axis-ID
protocol, while `Report.Chart(series=[...])` controls which metadata-defined
members are displayed.

### `Report.Chart`

Adds one 2D chart section.

| Parameter | Type | Default | Meaning |
| --- | --- | --- | --- |
| `title` | `str` | `"2D Chart"` | Section title. |
| `resources` | list of Case objects or IDs, or `None` | `None` | Cases shown by the chart. `None` selects up to 10 report Cases. |
| `x` | built-in X variable | `"alpha"` | X-axis variable. |
| `y` | list of built-in Y variables or `ChartVariable` | `["CL"]` | One to five Y axes. |
| `comparison` | `"absolute"` or `"delta"` | `"absolute"` | Value mode. Delta requires a reference Case and cannot use log scale. |
| `x_range` | `(min, max) \| None` | `None` | Manual X range. |
| `y_ranges` | mapping from selected Y variable to `(min, max)` or `None` | `None` | Manual ranges for selected Y axes. |
| `log_scale` | `bool` | `False` | Use logarithmic Y axes. |
| `style` | `"case"` or `"variable"` | `"case"` | `"case"` colors by Case and lines by variable; `"variable"` colors by variable and lines by Case. |
| `background_view` | `"left"`, `"back"`, `"top"`, or `None` | `None` | Geometry background for coordinate charts. Valid combinations are coordinate X with left/top and coordinate Y with back/top. |
| `force_scope` | `"total"`, `"faces"`, `"body_groups"`, or `None` | `None` | Force-history scope. Only valid for pseudo/physical step charts with a force Y variable. |
| `force_scope_ids` | `list[str] \| None` | `None` | IDs required by `faces` or `body_groups`. |
| `series` | `list[str] \| None` | `None` | Selected series for metadata-defined CSV/UDD variables. |
| `series_display` | `"individual"`, `"cumulative"`, `"summed"`, or `None` | `None` | Display mode for metadata-defined series. |

Discover fixed variables:

```python
Report.Chart.supported_x_variables()
Report.Chart.supported_y_variables()
```

`supported_y_variables()` returns only the fixed force and moment variables
accepted directly as strings in `y`. It is not a query of Case result metadata
and never returns residual, CFL, CSV, UDD, face, or body-group series. Those
belong in `Report.ChartVariable` plus `series`, as described above.

Compatibility rules:

- `alpha`, `beta`, `velocity`, `first_layer_thickness`, and
  `surface_max_edge_length` accept the fixed total-force variables `CL`, `CD`,
  `CFx`, `CFy`, `CFz`, `CMx`, `CMy`, and `CMz`.
- `pseudo_step` and `physical_step` accept fixed force-history variables and
  `Report.ChartVariable` values.
- `coordinate_x` and `coordinate_y` accept only `Report.ChartVariable` values.
- Metadata series names such as residual components belong in `series`, not in
  `y`.

### `Report.Visualization`

Adds one 3D visualization section.

| Parameter | Type | Default | Meaning |
| --- | --- | --- | --- |
| `title` | `str` | `"Visualization"` | Section title. |
| `resources` | list of resource objects or IDs, or `None` | `None` | Selected resources. All must have the same type. `None` selects the first eight Cases, or the first eight resources of the report's first resource type when there are no Cases. |
| `layout` | `"grid"` or `"single"` | `"grid"` | View layout. |
| `resolution` | `"low"`, `"high"`, or `None` | `None` | Case rendering resolution. Case defaults to low; non-Case resources always use high and reject `"low"`. |
| `setup` | `VisualizationSetup`, list, mapping, Scene visualization setting, or `None` | empty | Global linked-view setup. At most one manual setup per output name. A Scene setting must match the visualization resource type. |
| `camera` | `Report.Camera`, Scene viewpoint, or `None` | `None` | Global camera for linked views. |
| `views` | mapping from resource ID to `Report.VisualizationView` or `{setup, camera}` | empty | Per-resource overrides. `setup` accepts the same manual or Scene setting forms as global `setup`; `camera` accepts a `Report.Camera` or Scene viewpoint. An overridden view is unlinked from global state. |

A new visualization selects up to eight resources when `resources` is omitted.
Explicit selections above eight are invalid.

Supported setup types by resource:

- Case: `surface`, `slice`, `isosurface`, `streamline`
- SurfaceMesh: `surface`
- VolumeMesh: `slice`
- Geometry: no setup controls

Example:

```python
surface = Report.VisualizationSetup(
    output="Surface output",
    type="surface",
    field="Cp",
    show=["wing"],
    hide=["farfield"],
    color_range=(-2.0, 1.0),
)

Report.Visualization(
    title="Surface pressure",
    resources=[baseline, variant],
    setup=surface,
    camera=Report.Camera(
        look_at=(0.0, 0.0, 0.0),
        up=(0.0, 0.0, 1.0),
        dimension=5.0,
        dimension_dir="width",
        unit="m",
    ),
    views={
        variant.id: {
            "setup": Report.VisualizationSetup(
                output="Surface output",
                type="surface",
                field="CfVec",
            ),
        },
    },
)
```

#### Capability discovery and cross-resource setup

Use `get_capabilities(resource)` to read the generated visualization Manifest
and `check_visualization_setup(setup, capabilities)` to check exact output,
field, and visibility-target references before report creation. The client does
not automatically translate names or validate setups during
`Report.create(...)`.

See [Configure visualizations across resources](docs/visualization-capabilities.md)
for the capability models, `body`/`body_0` naming rules, compatibility checks,
AI selection policy, and global-versus-per-resource setup examples. See
[Reuse saved Scenes](docs/scenes.md) before applying one Workbench Scene to
multiple resources.

### `Report.Camera`

| Parameter | Type | Default | Meaning |
| --- | --- | --- | --- |
| `position` | 3-number tuple or `None` | `None` | Camera position vector. |
| `look_at` | 3-number tuple or `None` | `None` | Camera target. |
| `pan_target` | 3-number tuple or `None` | `None` | Pan target. |
| `up` | 3-number tuple or `None` | `None` | Camera up vector. |
| `dimension` | positive number or `None` | `None` | Framing dimension. |
| `dimension_dir` | `"width"`, `"height"`, `"diagonal"`, or `None` | `None` | Meaning of `dimension`. |
| `unit` | `"m"`, `"cm"`, `"mm"`, `"inch"`, `"ft"`, or `None` | `None` | Physical input unit for camera target/framing lengths. For a global camera, `None` means metres; for a per-resource camera, `None` means persisted model coordinates. |

A per-resource `Report.Camera` with an explicit `unit` and a per-resource Scene
viewpoint both read the target SimulationJSON and convert physical lengths to
model coordinates automatically. Scene viewpoint lengths are canonical metres.

### `Report.VisualizationSetup`

Every setup requires:

| Parameter | Type | Default | Meaning |
| --- | --- | --- | --- |
| `output` | `str` | required | Exact output name from the resource metadata. |
| `type` | `"surface"`, `"slice"`, `"isosurface"`, `"streamline"` | required | Output category. |
| `field` | `str \| None` | `None` | Field name. Required for field coloring options such as ranges, themes, clipping, contours, color maps, units, or time frames. |
| `show` | `list[str] \| None` | `None` | Entity IDs to show. |
| `hide` | `list[str] \| None` | `None` | Entity IDs to hide. IDs cannot also appear in `show`. |

Common field-display parameters:

| Parameter | Type | Default | Meaning |
| --- | --- | --- | --- |
| `log_scale` | `bool \| None` | `None` | Field logarithmic scale. |
| `range` | `(min, max) \| None` | `None` | Effective field range used for rendering. When `color_range` is omitted, this value is also used as the UI's color-scale bounds. |
| `color_range` | `(min, max) \| None` | `None` | Color-scale bounds saved by the UI. When `range` is omitted, this value is also used as the effective rendering range. |
| `theme` | `str \| None` | `None` | Color theme. Automatically set to `"custom"` for `custom_colors`. |
| `solid_color` | non-negative `int \| None` | `None` | Solid-color value used by the UI. |
| `show_color_map` | `bool \| None` | `None` | Show the color map. |
| `visualizer` | `"lic" \| None` | `None` | LIC field visualizer. |
| `unit` | `str \| None` | `None` | Display unit for the field. |
| `use_local_value` | `bool \| None` | `None` | Use resource-local field range. |
| `time_frame` | non-negative `int \| None` | `None` | Time-frame index. |

When only one of `range` and `color_range` is provided, its value is persisted
to both fields. When both are provided, `range` controls rendering and
`color_range` is preserved as the UI's color-scale bounds, so explicitly
different values remain distinct.

`solid_color` and non-field geometry settings may be used without `field`.

Clipping parameters:

| Parameter | Type | Default | Meaning |
| --- | --- | --- | --- |
| `clip` | `"none"`, `"above"`, `"below"`, `"range"`, or `None` | `None` | Clip mode. |
| `clip_value` | number or `None` | `None` | Threshold for `above` or `below`. |
| `clip_range` | `(min, max) \| None` | `None` | Required for `clip="range"` and invalid for other modes. |

Contour parameters are valid only for `surface` and `slice`:

| Parameter | Type | Default |
| --- | --- | --- |
| `contour_mode` | `"surface"`, `"contours"`, `"both"`, or `None` | `None` |
| `contour_steps` | positive `int \| None` | `None` |
| `contour_line_color` | non-negative `int \| None` | `None` |

Streamline parameters are valid only for `streamline`:

| Parameter | Type | Default |
| --- | --- | --- |
| `streamline_direction` | `"upstream"`, `"downstream"`, `"both"`, or `None` | `None` |
| `render_type` | `"ribbon"`, `"line"`, or `None` | `None` |
| `tube_width` | positive number or `None` | `None` |
| `tube_width_unit` | `str \| None` | `None` |
| `ribbon_width` | positive number or `None` | `None` |
| `ribbon_width_unit` | `str \| None` | `None` |
| `ribbon_angle_scale` | positive number or `None` | `None` |

Additional per-entity and custom-color parameters:

| Parameter | Type | Default | Meaning |
| --- | --- | --- | --- |
| `slice_variants` | mapping from ID to `"flat"` or `"crinkled"`, or `None` | `None` | Slice rendering per entity; valid only for `slice`. |
| `wireframes` | mapping from ID to `bool`, or `None` | `None` | Wireframe state per entity. |
| `custom_colors` | list of `(position, color)` or `None` | `None` | At least two unique positions from 0 to 100; colors use `#RRGGBB` or `#RRGGBBAA`. Only one setup in a visualization may define it. |

## Limits and validation behavior

- A report supports 1–50 resources.
- A new visualization defaults to the first 8 compatible resources and supports
  at most 8 resources of one type.
- Applying a report to new data uses the first 8 compatible visualization
  resources.
- A chart supports at most 10 Cases and 1–5 distinct Y axes.
- Explicit section resource selections must refer to resources in the report.
- A reference must be a Case included in the report.
- Unsupported combinations fail before the Report create/update request is sent.
  Most validation is local; compiling a physical per-resource camera can first
  read the target resource's SimulationJSON.

## `configJson` compatibility

The generated payload follows schema version `1` from the Report UI. Its
canonical grid layout contains only the `lg` key with 48 columns. Configurations
created by older client versions may also contain `md` and `sm`; the client
accepts those legacy keys when loading and omits them the next time it
serializes the configuration, matching the UI normalizer.

The internal persisted models also accept current UI state that is not exposed
as a high-level constructor parameter, including per-chart overrides,
`clipAreaType`, and visualization diagnostic conditions. Loading a report,
performing a metadata-only `update()`, or using `apply_to_new()` preserves the
applicable persisted state. Supplying `resources` and `sections` to `update()`
instead rebuilds the configuration from the documented high-level classes.

The client currently recognizes the UI's `summary`, `visualization`, and
`chart2d` section types. Unlike the UI's forward-compatible normalizer, it
rejects unknown section types and unknown fields instead of silently discarding
them. A new persisted UI section or field therefore requires a corresponding
client model update before that report can be loaded.

## License

This package is licensed under the GNU Lesser General Public License v2.1 only.
See [LICENSE](LICENSE) for the complete terms.

## Development

```bash
python3.12 -m venv .venv
.venv/bin/python -m pip install -e ".[dev]"
.venv/bin/ruff check src tests
.venv/bin/ruff format --check src tests
.venv/bin/pytest
./scripts/build-wheel.sh
```

The build script creates a wheel and source distribution under `dist/` and
validates their package metadata with Twine. User-visible release history is in
[CHANGELOG.md](CHANGELOG.md).

The persisted report schema is versioned independently from the Flow360 Python
client. Schema changes must remain compatible with existing schema version
`1`, or introduce an explicit new schema version.
