{"componentChunkName":"component---src-templates-mdproject-jsx","path":"/projects/bkv_gas_injection","result":{"data":{"allGithubFile":{"nodes":[{"base":"bkv_gas_injection.md","relativeDirectory":"projects","childMarkdownRemark":{"id":"e5aacb1a-78ba-5135-9fc2-7388e22f7254","html":"<h1>Gas Injection Optimization App - BKV Corporation</h1>\n<p>Data Scientist/Developer: Manuel Solalinde <a href=\"mailto:manolosolalinde@gmail.com\">(manolosolalinde@gmail.com)</a></p>\n<p>On gas-lift wells, operators routinely over- or under-inject gas, leaving production on the table or wasting injection. This production data-analytics platform helps reservoir and production engineers <strong>right-size gas injection on gas-lift wells</strong>: for each well it turns noisy daily production and injection history into a single recommended injection rate, produced by a data-driven model trained against engineer-labeled data. On top of the per-well recommendation it generates the report emailed to operators, ranking every injecting well by how far it has drifted from its recommendation — contributing to a measurable improvement in net sales.</p>\n<blockquote>\n<p><em>Note: In production use, the tool flagged over-injecting wells and guided operators to reduce injection, contributing to a measurable improvement in net sales. Specific production and financial figures are confidential client data and are omitted.</em></p>\n</blockquote>\n<h2>Applications</h2>\n<p>The platform bundles five sub-applications, selected from a dropdown:</p>\n<ul>\n<li><strong>Gas Injection Estimator</strong> <em>(production)</em> — interactive, single-well analysis. Shows the well's full timeseries, the model's cluster analysis, and the recommended injection rate.</li>\n<li><strong>Gas Injection Report</strong> <em>(production)</em> — the report emailed to operators, ranking every injection well by how far current injection deviates from the recommendation. Regenerates on demand via a parallel Lambda fan-out.</li>\n<li><strong>Gas Injection Report History</strong> <em>(production)</em> — tracks which wells were flagged across all historical reports, counts consecutive flags, and surfaces wells that have since resolved.</li>\n<li><strong>Gas Injection Report Preview</strong> <em>(research)</em> — single-well report generator for testing and validating the per-well computation on demand.</li>\n<li><strong>Gas Injection Labeler</strong> <em>(research)</em> — manual annotation tool used to build the training data behind the model's penalty weights.</li>\n</ul>\n<h2>The Recommendation Model</h2>\n<p>The core of the app is a model that converts a well's daily history into one injection recommendation. Rather than a closed-form physics calculation, it groups the history into stable operating regimes and scores them. The pipeline:</p>\n<ol>\n<li><strong>Clean</strong> — drop shut-in-extended days to remove transient recovery noise.</li>\n<li><strong>Cluster</strong> — fit a <code class=\"language-text\">DecisionTreeRegressor(criterion=\"absolute_error\")</code> to injection rate versus time, segmenting the history into stretches that each held a roughly constant injection target; adjacent similar segments are merged into a <strong>cluster</strong>.</li>\n<li><strong>Aggregate</strong> — compute per-cluster statistics: mean injection/production, stability ratios, production trend, net production-minus-injection, and more.</li>\n<li><strong>Filter</strong> — discard unreliable clusters (too little injection, too few days, or excessive variance).</li>\n<li><strong>Score</strong> — each surviving cluster gets a penalty (lower is better) combining a production penalty, a production-minus-injection efficiency penalty, an injection-stability penalty, and (optionally) production-volatility and trend penalties.</li>\n<li><strong>Recommend</strong> — invert the score and select the best-ranked cluster; its mean injection target becomes the recommendation.</li>\n</ol>\n<p>The penalty weights are the only part of the recommendation learned from data — see below.</p>\n<h2>Model Training (data-driven weights)</h2>\n<p>The penalty weights are fitted <strong>offline</strong> against ground truth collected with the Labeler:</p>\n<ul>\n<li><strong>Ground truth</strong> — a set of usable wells, each labeled by an engineer with their chosen optimal injection rate.</li>\n<li><strong>Feature cache</strong> — a notebook runs the exact same clustering pipeline the live app uses and caches each well's cluster table, with diagnostics confirming the clustering almost always produces a good candidate, so the weights' only job is to <strong>pick the right cluster</strong>.</li>\n<li><strong>Objective</strong> — a custom loss that primarily minimizes the share of wells missed beyond a tolerance, with light elastic-net (L1 + L2) regularization on the weights.</li>\n<li><strong>Optimization</strong> — a train/test split and <strong>Bayesian optimization</strong> (<code class=\"language-text\">skopt.gp_minimize</code>, a Gaussian-process surrogate) in a coordinate stage followed by a joint refinement.</li>\n</ul>\n<blockquote>\n<p><em>Note: Specific tuned weight values, well counts and model accuracy figures are derived from proprietary client data and are omitted.</em></p>\n</blockquote>\n<h2>Design decisions &#x26; trade-offs</h2>\n<ul>\n<li><strong>Data-driven penalty weights over a closed-form physics calculation.</strong> Engineers can label ground-truth optimal rates, and the clustering pipeline already surfaces a good candidate cluster, so the learned weights only have to <strong>pick the right one</strong> — a far easier and more robust problem than modeling the physics directly.</li>\n<li><strong><code class=\"language-text\">DecisionTreeRegressor</code> clustering of injection-rate-versus-time.</strong> This segments history into stretches of roughly constant injection target, matching how operators actually think about a well rather than imposing an arbitrary windowing.</li>\n</ul>\n<h2>Hard problems solved</h2>\n<ul>\n<li><strong>Custom training objective.</strong> A loss that primarily minimizes the share of wells whose recommendation is missed beyond a tolerance, with light elastic-net (L1 + L2) regularization on the weights to keep the solution simple and generalizable.</li>\n<li><strong>Bayesian optimization over a train/test split.</strong> A Gaussian-process surrogate (<code class=\"language-text\">skopt.gp_minimize</code>) tunes the weights in a coordinate stage followed by a joint refinement, avoiding overfitting to the labeled set.</li>\n<li><strong>Train/inference parity.</strong> A shared engineering library is imported by <strong>both</strong> the live app and the offline training notebooks, guaranteeing the trained weights are evaluated against the exact pipeline that runs in production.</li>\n</ul>\n<h2>Report</h2>\n<p>The Gas Injection Report ranks every injecting well by the difference between its recent injection and its recommendation (using the operator's saved decision as the baseline if one exists, else the model recommendation). Regeneration fans out <strong>Lambda</strong> functions in parallel to recompute every well's metrics and uploads the results to <strong>S3</strong>. Three views are available: an email-ready <strong>CSV view</strong>, a fully filterable <strong>Research view</strong> (GLR, production and injection thresholds, route, minimum delta), and an unfiltered <strong>Raw view</strong>. Engineers can override the model for any well from the sidebar; <strong>operator decisions are stored in DynamoDB</strong> and replace the recommendation as the baseline across all views.</p>\n<h2>Architecture</h2>\n<p>Python + <strong>Streamlit</strong> web app with <strong>Plotly</strong> visualization. Per-well daily production and injection data lives in <strong>AWS S3</strong> as parquet, read on demand. The report regenerates through a <strong>parallel Lambda fan-out</strong>; operator overrides persist in <strong>DynamoDB</strong>. The recommendation logic lives in a shared engineering library imported by <strong>both</strong> the live app and the offline <strong>Jupyter</strong> training notebooks, guaranteeing the trained weights are evaluated against the exact pipeline that runs in production. User documentation is published with <strong>MkDocs</strong>.</p>\n<blockquote>\n<p><em>Note: Screenshots are omitted, as they display proprietary client data. Available on request, subject to client approval.</em></p>\n</blockquote>","frontmatter":{"title":"Gas Injection Optimization App","image_url":"./images/covers/gas_injection.png","description":"Data-driven app that recommends optimal gas-injection rates for gas-lift wells.","technologies":"Python, Streamlit, Pandas, NumPy, Scikit-learn (DecisionTreeRegressor clustering), scikit-optimize (Bayesian optimization), Plotly, AWS (S3, Lambda, DynamoDB), Parquet, Jupyter, MkDocs.","date":"2026-02-01T00:00:00.000Z"}},"user":"manolosolalinde","repository":"cv"}]},"site":{"siteMetadata":{"title":"Manuel Solalinde's Portfolio","description":"Manuel Solalinde's Portfolio and Featured Projects","author":"Manuel Solalinde"}}},"pageContext":{"uid":"bkv_gas_injection.md","rootQuery":"\n    query mdProjectQuery($uid: String) {\n        allGithubFile(filter: {base: {eq: $uid}, relativeDirectory: {eq: \"projects\"}}) {\n            nodes {\n              base\n              relativeDirectory\n              childMarkdownRemark {\n                id\n                html\n                frontmatter {\n                  title\n                  image_url\n                  description\n                  technologies\n                  date\n                }\n              }\n              user\n              repository\n            }\n        }\n        site {\n            siteMetadata {\n                title\n                description\n                author\n            }\n        }\n    }\n"}},"staticQueryHashes":["3649515864"]}