Skip to main content

energyml_geometry/
lib.rs

1//! From an EnergyML representation to points a program can draw.
2//!
3//! The storage layer answers "what is in this package" and "give me that
4//! array". This one answers the question an application actually has: **where
5//! is this surface, and what shape is it?**
6//!
7//! # The thing nobody expects
8//!
9//! An EnergyML array is not a file offset. `AbstractPoint3dArray` has half a
10//! dozen concrete forms and only one of them is "read it from HDF5":
11//!
12//! | Form | Where the numbers are |
13//! |---|---|
14//! | `Point3dExternalArray` / `Point3dHdf5Array` | in the external file |
15//! | `Point3dLatticeArray` | **nowhere** - an origin, direction vectors and spacings, from which the points are *computed* |
16//! | `Point3dZValueArray` | x and y from a supporting geometry (usually a lattice), z from an array |
17//! | `Point3dParametricArray` | on parametric lines, evaluated from control points |
18//! | `Point3dFromRepresentationLatticeArray` | in *another* representation |
19//!
20//! On the reference corpus that is 71 external arrays against 19 that are not,
21//! so a reader that only knows HDF5 silently loses every 2D grid and every
22//! wellbore trajectory - and loses them as *empty geometry*, which looks like
23//! a working program showing nothing. The same is true of plain numeric arrays:
24//! `DoubleConstantArray` and `DoubleLatticeArray` carry no data either.
25//!
26//! So [`values`] and [`points`] resolve **every** form, and the ones that are
27//! not implemented yet fail by name instead of returning nothing.
28//!
29//! # Two places a reader quietly gets it wrong
30//!
31//! Both were found by running this crate over the reference corpus, and both
32//! produce a plausible-looking result rather than an error:
33//!
34//! - **Patch node numbering.** A triangle's indices may be numbered inside its
35//!   own patch or across the whole representation - FESAPI writes one, resqpy
36//!   the other. [`geometry`] decides from the numbers and refuses what fits
37//!   neither, and [`Patch::node_offset`] records the patch's place.
38//! - **Lattice axis order.** Dimensions are listed slowest first, so the *last*
39//!   one varies fastest. Reversing it keeps the point count exactly right and
40//!   transposes the surface.
41//!
42//! # Where it sits
43//!
44//! Above the storage layer, never inside it (ADR-0002). Everything here takes objects already in memory plus a [`ValueSource`]
45//! for the arrays, so an ETP dataspace works exactly as a file does.
46
47#![forbid(unsafe_code)]
48#![warn(missing_docs)]
49
50pub mod clip;
51pub mod connection;
52pub mod defined;
53pub mod derived;
54pub mod error;
55pub mod fence;
56pub mod follow;
57pub mod geometry;
58pub mod heads;
59pub mod ijk;
60pub mod min_curvature;
61pub mod parametric;
62pub mod parent;
63pub mod plane;
64pub mod points;
65pub mod readers;
66pub mod scene;
67pub mod section;
68pub mod shape;
69pub mod source;
70pub mod stations;
71pub mod transfer;
72pub mod tube;
73pub mod unstructured;
74pub mod values;
75pub mod well;
76pub mod wells;
77
78pub use energyml_domain::{Crs, CrsKind, VerticalDirection, VerticalQuantity};
79/// Units and coordinate reference systems, shared with every other consumer.
80///
81/// Re-exported so a caller working on geometry does not have to name a second
82/// crate for the CRS of the surface it just read. The definitions live in
83/// [`energyml_domain`] because a WITSML well needs them just as much, and a
84/// crate called *geometry* is the wrong place to keep them.
85pub use energyml_domain::{crs, reproject, units};
86pub use error::{Error, Result};
87pub use geometry::{Geometry, Patch, Polyhedra, Topology};
88pub use points::Point;
89pub use readers::{Readers, RepresentationReader};
90pub use scene::{Attribute, AttributeScope, Placement, Related, SceneObject};
91pub use source::ValueSource;