Compare commits

...

1 Commits
main ... v8

Author SHA1 Message Date
FunctionsAPI
39e9b4af9c Automatic push from FunctionsAPI 2025-06-11 14:31:57 +00:00
5 changed files with 3707 additions and 1 deletions

3421
Cargo.lock generated Normal file

File diff suppressed because it is too large Load Diff

11
Cargo.toml Normal file
View File

@ -0,0 +1,11 @@
[package]
edition = "2024"
name = "web"
version = "0.1.0"
[dependencies]
fathom-function = { git = "ssh://git@github.com/fathom-io/pipeline-calculations.git", branch = "FTHM-12741/adnoc-ingestion" }
pipeline-application = { git = "ssh://git@github.com/fathom-io/pipeline-calculations.git", branch = "FTHM-12741/adnoc-ingestion" }
serde = { version = "1.0.219", features = ["derive"] }
tokio = { version = "1.43.0", features = ["macros", "rt-multi-thread"] }
uuid = { version = "1" }

121
README.md
View File

@ -1,2 +1,121 @@
# 9e08b596bc6b495ca719bcfb6c5a0ace
# Runs the ILI clustering algorithm
Clustering criteria for pipeline defects refer to the guidelines or criteria that are used to
determine whether a group of defects in a pipeline should be considered a "cluster." These
criteria are typically based on a combination of factors, including the size, location, and
severity of the defects, as well as the spacing between them.
There are several different clustering criteria that can be used for pipeline defects,
depending on the specific context and application. Some common clustering criteria include:
- *Percentage of wall loss*: This criterion considers the percentage of the pipeline wall that has
been lost due to corrosion or other defects. A group of defects that collectively represent a
certain percentage of wall loss (e.g., 10%) may be considered a cluster.
- *Spacing between defects*: This criterion considers the distance between individual defects in a
pipeline. If the spacing between defects falls below a certain threshold (e.g., 1 meter), they
may be considered a cluster.
- *Geographic location*: This criterion considers the geographic location of defects within a
pipeline. If defects are clustered within a certain area of the pipeline (e.g., a bend or a
weld), they may be considered a cluster.
- *Severity of defects*: This criterion considers the severity or consequence of individual
defects. If a group of defects collectively represent a significant risk of failure or leakage,
they may be considered a cluster.
Pipeline operators typically use clustering criteria as part of their inspection and
maintenance programs to identify and prioritize areas of the pipeline that require attention.
By identifying and addressing clusters of defects, operators can help prevent failures and
ensure the safe and reliable operation of their pipelines.
Examining the clustering of pipeline defects is a crucial aspect of pipeline integrity
management. This involves evaluating the spacing between defects in a pipeline to determine the
likelihood of larger defects clustering together. Specific conditions must be met for this
distance to be considered acceptable. If these conditions are met, the possibility of defect
clustering cannot be overlooked.
## Clustering rules
Rules are used to configure the clustering algorithm. There are 11 standard rules and then a 12th custom rule.
Each rule is based on a length and circumferential criteria. If any two anomalies have a horizontal
separation less than the length criteria and a circumferential separation less than the circumferential
criteria the should be clustered.
The rules are details in the table below.
| Rule no. | Length Criteria | Circumferential Criteria |
| -------- | ------------------------------------------- | ------------------------------------------- |
| Rule 1 | Minimum (L1,L2) | Minimum (W1,W2) |
| Rule 2 | Minimum (6*wall thickness, Minimum (L1,L2)) | Minimum (6*wall thickness, Minimum (W1,W2)) |
| Rule 3 | Minimum (wall thickness, Minimum (L1,L2)) | Minimum (wall thickness, Minimum (W1,W2)) |
| Rule 4 | Minimum (L1,L2) | Minimum (wall thickness, Minimum (W1,W2)) |
| Rule 5 | Average (L1,L2) | Average (W1,W2) |
| Rule 6 | Wall thickness | Wall thickness |
| Rule 7 | 3*Wall thickness | 3*Wall thickness |
| Rule 8 | 2*Minimum (L1,L2) | 2*Minimum (W1,W2) |
| Rule 9 | Minimum (6*wall thickness, Average (L1,L2)) | Minimum (6*wall thickness, Average (W1,W2)) |
| Rule 10 | 1 inch = 25.4 mm | 6*wall thickness |
| Rule 11 | 1 inch = 25.4 mm | (3 × MAOP × OD) / (SF × SMYS × WJF) |
| Rule 12 | User Configurations | User Configurations |
## Input
### Arguments
- `org_id`: as string which should be a valid `uuid` for the organization
- `project_id`: the id of the data project where the pipeline data is found
- `pipeline_id`: an `array` of `strings` which should be valid UUIDs for pipelines
- `size_settings`: a string representing the size setting for clustering, possible values
- `original`
- `tolerance`
- `interaction`: a string representing the interaction setting for clustering, possible values
- `enabled`
- `disabled`
- `surface_location`: a string representing the surface location setting for clustering, possible values
- `matching`
- `any`
- `rule`: a string representing the clustering rule to use, possible values
- `rule1`
- `rule2`
- `rule3`
- `rule4`
- `rule5`
- `rule6`
- `rule7`
- `rule8`
- `rule9`
- `rule10`
- `rule11`
## Creating the function on the platform
To create this function on the platform using the `cli` set up the port forwarding as shown in README.
Then run the following command to create the function.
```bash
cargo run functions create \
-f functions/ili_clustering \
-d "Runs the ILI clustering algorithm" \
-i org_id=string \
-i project_id=string \
-i pipeline_id=array \
-i ili_id=array \
-i size_settings=string \
-i interaction=string \
-i surface_location=string \
-i rule=string
```
## Testing the function locally
You can run and test the function locally by running
```bash
cargo run
```
Then you can check it work with `curl` as follows
```bash
curl localhost:8080 -d $(jq '. | tojson' functions/ili_clustering/example_input.json)
```

14
example_input.json Normal file
View File

@ -0,0 +1,14 @@
{
"ili_id": [
"d0dd7c6b-6c54-4149-b46c-5e5b033fe6dd"
],
"interaction": "disabled",
"org_id": "2cbfe270-d195-48ad-aed1-24145924635c",
"pipeline_id": [
"01966d47-1d4c-7751-a1f1-0617caa3a00d"
],
"project_id": "680b61b0aedd6f9e639d8699",
"rule": "rule1",
"size_settings": "original",
"surface_location": "matching"
}

141
src/main.rs Normal file
View File

@ -0,0 +1,141 @@
use fathom_function::tracing;
use pipeline_application::application::{
AnomalySizeSetting, Application, ClusteringConfig, Interaction as ClusteringInteraction,
Rule as ClusteringRule, SurfaceLocationSensitivity,
};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[fathom_function::function]
async fn ili_clustering(input: Input) -> Result<Output, String> {
let app = Application::new_from_compile_env(input.org_id, input.project_id).unwrap();
for (pipeline_id, ili_id) in input.pipeline_id.into_iter().zip(input.ili_id) {
app.ili_clustering(pipeline_id, ili_id, &input.config)
.await
.map_err(|err| {
tracing::error!(%pipeline_id, %ili_id, ?err, "Error running clustering algorithm");
format!("{err:?}")
})?;
}
Ok(Output {
status: "Success".to_owned(),
})
}
#[derive(Debug, Serialize)]
struct Output {
status: String,
}
#[derive(Debug, Clone, Copy, Deserialize)]
#[serde(rename_all = "snake_case")]
enum Rule {
Rule1,
Rule2,
Rule3,
Rule4,
Rule5,
Rule6,
Rule7,
Rule8,
Rule9,
Rule10,
Rule11,
}
#[derive(Debug, Clone, Copy, Deserialize)]
#[serde(rename_all = "snake_case")]
enum SizeSettings {
Original,
Tolerance,
}
#[derive(Debug, Clone, Copy, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum Interaction {
Enabled,
Disabled,
}
#[derive(Debug, Clone, Copy, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum SurfaceLocation {
Matching,
Any,
}
#[derive(Debug, Deserialize)]
struct Input {
org_id: Uuid,
project_id: String,
pipeline_id: Vec<Uuid>,
ili_id: Vec<Uuid>,
#[serde(flatten)]
config: Config,
}
#[derive(Debug, Deserialize)]
struct Config {
size_settings: SizeSettings,
interaction: Interaction,
surface_location: SurfaceLocation,
rule: Rule,
}
impl From<Rule> for ClusteringRule {
fn from(value: Rule) -> Self {
match value {
Rule::Rule1 => Self::Rule1,
Rule::Rule2 => Self::Rule2,
Rule::Rule3 => Self::Rule3,
Rule::Rule4 => Self::Rule4,
Rule::Rule5 => Self::Rule5,
Rule::Rule6 => Self::Rule6,
Rule::Rule7 => Self::Rule7,
Rule::Rule8 => Self::Rule8,
Rule::Rule9 => Self::Rule9,
Rule::Rule10 => Self::Rule10,
Rule::Rule11 => Self::Rule11,
}
}
}
impl From<SizeSettings> for AnomalySizeSetting {
fn from(value: SizeSettings) -> Self {
match value {
SizeSettings::Original => Self::Original,
SizeSettings::Tolerance => Self::WithTolerance,
}
}
}
impl From<SurfaceLocation> for SurfaceLocationSensitivity {
fn from(value: SurfaceLocation) -> Self {
match value {
SurfaceLocation::Matching => Self::Matching,
SurfaceLocation::Any => Self::Any,
}
}
}
impl From<Interaction> for ClusteringInteraction {
fn from(value: Interaction) -> Self {
match value {
Interaction::Enabled => Self::Enabled,
Interaction::Disabled => Self::Disabled,
}
}
}
impl From<&Config> for ClusteringConfig {
fn from(value: &Config) -> Self {
Self {
interaction: value.interaction.into(),
surface_location: value.surface_location.into(),
size_settings: value.size_settings.into(),
rule: value.rule.into(),
}
}
}