Skip to content

Commit

Permalink
ENH: Add inplace volumes result and schema
Browse files Browse the repository at this point in the history
  • Loading branch information
mferrera committed Dec 17, 2024
1 parent 0252737 commit 80254e5
Show file tree
Hide file tree
Showing 4 changed files with 121 additions and 0 deletions.
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ dev = [
"coverage>=4.1",
"ert",
"hypothesis",
"jsonschema",
"mypy",
"pandas-stubs",
"pyarrow-stubs",
Expand Down
Empty file.
71 changes: 71 additions & 0 deletions src/fmu/dataio/_products/inplace_volumes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
from __future__ import annotations

from typing import TYPE_CHECKING, Final, List, Literal, Optional, Union

from pydantic import BaseModel, Field, RootModel
from pydantic.json_schema import GenerateJsonSchema

if TYPE_CHECKING:
from typing import Any, Mapping

# These are to be used when creating the 'product' key in metadata.
VERSION: Final[str] = "0.1.0"
SCHEMA: Final[str] = (
"https://main-fmu-schemas-prod.radix.equinor.com/schemas"
f"/products/volumes/{VERSION}/inplace_volumes.json"
) # TODO: This URL is as-yet undecided.


class InplaceVolumesResultRow(BaseModel):
"""Represents a row in a static inplace volumes export.
These fields are the current agreed upon standard result. Changes to this model
should increase the version number in a way that corresponds to the schema
versioning specification (i.e. they are a patch, minor, or major change)."""

FLUID: Literal["oil", "gas", "water"]
ZONE: str
REGION: str
FACIES: Optional[Union[str]] = Field(default=None)
LICENSE: Optional[Union[str, int]] = Field(default=None)

BULK: Optional[float] = Field(default=None, ge=0.0)
NET: Optional[float] = Field(default=None, ge=0.0)
PORV: Optional[float] = Field(default=None, ge=0.0)
HCPV: Optional[float] = Field(default=None, ge=0.0)
STOIIP: Optional[float] = Field(default=None, ge=0.0)
GIIP: Optional[float] = Field(default=None, ge=0.0)
ASSOCIATEDGAS: Optional[float] = Field(default=None, ge=0.0)
ASSOCIATEDOIL: Optional[float] = Field(default=None, ge=0.0)


class InplaceVolumesResult(RootModel):
"""Represents the resultant static inplace volumes csv file, which is naturally a
list of rows.
Consumers who retrieve this csv file must reading it into a json-dictionary
equivalent format to validate it against the schema."""

root: List[InplaceVolumesResultRow]


class InplaceVolumesJsonSchema(GenerateJsonSchema):
"""Implements a schema generator so that some additional fields may be added."""

def generate(
self,
schema: Mapping[str, Any],
mode: Literal["validation", "serialization"] = "validation",
) -> dict[str, Any]:
json_schema = super().generate(schema, mode=mode)
json_schema["$schema"] = self.schema_dialect
json_schema["$id"] = SCHEMA
json_schema["version"] = VERSION

return json_schema


def dump() -> dict[str, Any]:
return InplaceVolumesResult.model_json_schema(
schema_generator=InplaceVolumesJsonSchema
)
49 changes: 49 additions & 0 deletions tests/test_export_rms/test_export_rms_volumetrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,18 @@
import unittest.mock as mock
from pathlib import Path

import jsonschema
import numpy as np
import pandas as pd
import pytest

import fmu.dataio as dataio
from fmu.dataio._logging import null_logger
from fmu.dataio._products.inplace_volumes import (
InplaceVolumesResult,
InplaceVolumesResultRow,
dump,
)
from tests.utils import inside_rms

logger = null_logger(__name__)
Expand Down Expand Up @@ -322,3 +328,46 @@ def test_rms_volumetrics_export_function(
assert "volumes" in metadata["data"]["content"]
assert metadata["access"]["classification"] == "restricted"
assert metadata["data"]["table_index"] == _TABLE_INDEX_COLUMNS


@inside_rms
def test_inplace_volumes_payload_validates_against_model(
exportvolumetrics,
monkeypatch,
):
"""Tests that the volume table exported is validated against the payload result
model."""

out = exportvolumetrics._export_volume_table()
with open(out.items[0].absolute_path) as f:
df = pd.read_csv(f).replace(np.nan, None).to_dict(orient="records")
InplaceVolumesResult.model_validate(df) # Throws if invalid


@inside_rms
def test_inplace_volumes_payload_validates_against_schema(
exportvolumetrics,
monkeypatch,
):
"""Tests that the volume table exported is validated against the payload result
schema."""

out = exportvolumetrics._export_volume_table()
with open(out.items[0].absolute_path) as f:
df = pd.read_csv(f).replace(np.nan, None).to_dict(orient="records")
jsonschema.validate(instance=df, schema=dump()) # Throws if invalid


@inside_rms
def test_inplace_volumes_export_and_result_columns_are_the_same(
mock_project_variable,
mocked_rmsapi_modules,
) -> None:
from fmu.dataio.export.rms.inplace_volumes import (
_TABLE_INDEX_COLUMNS,
_VOLUMETRIC_COLUMNS,
)

export_columns = _TABLE_INDEX_COLUMNS + _VOLUMETRIC_COLUMNS
result_columns = InplaceVolumesResultRow.model_fields.keys()
assert set(export_columns) == set(result_columns)

0 comments on commit 80254e5

Please sign in to comment.