alterzick commited on
Commit
1c96387
·
verified ·
1 Parent(s): c76d903

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +6 -4
  2. index.html +785 -19
  3. prompts.txt +3 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Dca
3
- emoji: 👁
4
- colorFrom: indigo
5
  colorTo: purple
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: dca
3
+ emoji: ⚛️
4
+ colorFrom: yellow
5
  colorTo: purple
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - QwenSite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,785 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8" />
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0"/>
6
+ <title>Decline Curve Analysis - Oil & Gas Well Production</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
9
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/xlsx/0.18.5/xlsx.full.min.js"></script>
10
+ <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"/>
11
+ <style>
12
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
13
+
14
+ body {
15
+ font-family: 'Inter', sans-serif;
16
+ }
17
+
18
+ .chart-container {
19
+ position: relative;
20
+ margin: auto;
21
+ width: 100%;
22
+ max-width: 900px;
23
+ height: 500px;
24
+ }
25
+
26
+ .upload-area {
27
+ border: 2px dashed #cbd5e1;
28
+ border-radius: 12px;
29
+ padding: 3rem 2rem;
30
+ text-align: center;
31
+ background-color: #f8fafc;
32
+ transition: all 0.3s ease;
33
+ cursor: pointer;
34
+ }
35
+
36
+ .upload-area:hover {
37
+ border-color: #4c51bf;
38
+ background-color: #ebf4ff;
39
+ }
40
+
41
+ .upload-area.highlight {
42
+ border-color: #2b6cb0;
43
+ background-color: #e6f0ff;
44
+ }
45
+
46
+ .info-card {
47
+ transition: all 0.3s ease;
48
+ border-left: 4px solid #4c51bf;
49
+ }
50
+
51
+ .info-card:hover {
52
+ transform: translateY(-2px);
53
+ box-shadow: 0 10px 25px rgba(0, 0, 0, 0.1);
54
+ }
55
+
56
+ .manual-input {
57
+ transition: all 0.3s ease;
58
+ border-left: 3px solid #2b6cb0;
59
+ }
60
+
61
+ .manual-input:hover {
62
+ transform: translateY(-1px);
63
+ box-shadow: 0 5px 15px rgba(0, 0, 0, 0.08);
64
+ }
65
+ </style>
66
+ </head>
67
+ <body class="bg-gradient-to-br from-slate-50 to-slate-100 min-h-screen">
68
+
69
+ <div class="container mx-auto px-4 py-10">
70
+ <div class="text-center mb-10">
71
+ <h1 class="text-4xl font-bold text-gray-800 mb-3">Decline Curve Analysis</h1>
72
+ <p class="text-lg text-gray-600 max-w-3xl mx-auto">
73
+ Enter current well information and upload your production data in Excel format to analyze production decline and forecast future performance.
74
+ Supports exponential, hyperbolic, and harmonic decline models.
75
+ </p>
76
+ </div>
77
+
78
+ <!-- Manual Input Section for Current Month and Production -->
79
+ <div class="bg-white rounded-2xl shadow-xl p-8 mb-8 max-w-4xl mx-auto transform hover:shadow-2xl transition-shadow duration-300">
80
+ <div class="flex items-center mb-6 text-blue-700">
81
+ <i class="fas fa-pen-alt text-2xl mr-3"></i>
82
+ <h2 class="text-2xl font-semibold">Current Well Information</h2>
83
+ </div>
84
+
85
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-6 mb-8 manual-input bg-sky-50 p-6 rounded-xl border border-sky-200">
86
+ <div class="mb-4">
87
+ <label for="currentMonthInput" class="block text-sm font-medium text-gray-700 mb-2">Current Month of Production</label>
88
+ <div class="relative">
89
+ <select id="currentMonthInput" class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500 appearance-none bg-white">
90
+ <option value="">-- Select Month --</option>
91
+ <option value="0">Current (0 months)</option>
92
+ <option value="1">1 Month Ago</option>
93
+ <option value="2">2 Months Ago</option>
94
+ <option value="3">3 Months Ago</option>
95
+ <option value="6">6 Months Ago</option>
96
+ <option value="9">9 Months Ago</option>
97
+ <option value="12">12 Months Ago</option>
98
+ <option value="18">18 Months Ago</option>
99
+ <option value="24">2 Years Ago</option>
100
+ <option value="36">3 Years Ago</option>
101
+ <option value="48">4 Years Ago</option>
102
+ <option value="60">5 Years Ago</option>
103
+ </select>
104
+ <div class="pointer-events-none absolute inset-y-0 right-0 flex items-center px-3 text-gray-700">
105
+ <i class="fas fa-chevron-down"></i>
106
+ </div>
107
+ </div>
108
+ <p class="mt-2 text-sm text-gray-500">How many months ago is this current data point?</p>
109
+ </div>
110
+
111
+ <div class="mb-4">
112
+ <label for="currentProductionInput" class="block text-sm font-medium text-gray-700 mb-2">Current Production Rate (BOPD)</label>
113
+ <div class="relative">
114
+ <input type="number" id="currentProductionInput" class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500" placeholder="Enter current production rate" min="0" step="0.1">
115
+ <div class="absolute inset-y-0 right-0 flex items-center px-3 bg-blue-50 rounded-r-lg">
116
+ <span class="text-sm text-blue-700 font-medium">BOPD</span>
117
+ </div>
118
+ </div>
119
+ <p class="mt-2 text-sm text-gray-500">Current oil production rate in barrels of oil per day</p>
120
+ </div>
121
+ </div>
122
+
123
+ <div class="bg-amber-50 p-4 rounded-lg border border-amber-200 mb-6">
124
+ <div class="flex">
125
+ <div class="flex-shrink-0">
126
+ <i class="fas fa-info-circle text-amber-500 text-xl"></i>
127
+ </div>
128
+ <div class="ml-3">
129
+ <h3 class="text-sm font-medium text-amber-800">Important Note</h3>
130
+ <p class="mt-1 text-sm text-amber-700">
131
+ These values will be used as reference points for your analysis. They will be included in the results section before you upload your Excel file with historical data.
132
+ </p>
133
+ </div>
134
+ </div>
135
+ </div>
136
+ </div>
137
+
138
+ <!-- Upload Section -->
139
+ <div class="bg-white rounded-2xl shadow-xl p-8 mb-8 max-w-4xl mx-auto transform hover:shadow-2xl transition-shadow duration-300">
140
+ <div class="flex items-center mb-6 text-blue-700">
141
+ <i class="fas fa-chart-line text-2xl mr-3"></i>
142
+ <h2 class="text-2xl font-semibold">Upload Production Data</h2>
143
+ </div>
144
+
145
+ <div id="dropZone" class="upload-area mb-6">
146
+ <i class="fas fa-cloud-upload-alt text-5xl text-gray-400 mb-4"></i>
147
+ <h3 class="text-xl font-medium text-gray-700 mb-2">Drag & Drop Excel File</h3>
148
+ <p class="text-gray-500 mb-4">or click to browse files</p>
149
+ <p class="text-sm text-gray-400">Supported: .xlsx, .xls | Example columns: Date, Rate (BOPD), Cumulative (STB)</p>
150
+ <input type="file" id="fileInput" accept=".xlsx, .xls" class="hidden"/>
151
+ </div>
152
+
153
+ <div class="flex justify-center">
154
+ <button id="uploadBtn" class="px-6 py-3 bg-blue-600 text-white font-medium rounded-lg shadow-md hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-opacity-50 transition duration-200 disabled:opacity-50 disabled:cursor-not-allowed" disabled>
155
+ <i class="fas fa-upload mr-2"></i>Process Data
156
+ </button>
157
+ </div>
158
+ </div>
159
+
160
+ <!-- Results Section -->
161
+ <div id="resultsSection" class="hidden bg-white rounded-2xl shadow-xl p-8 max-w-6xl mx-auto">
162
+ <div class="flex items-center mb-6 text-green-700">
163
+ <i class="fas fa-check-circle text-2xl mr-3"></i>
164
+ <h2 class="text-2xl font-semibold">Analysis Results</h2>
165
+ </div>
166
+
167
+ <!-- Key Metrics Row -->
168
+ <div class="grid grid-cols-1 md:grid-cols-5 gap-4 mb-8">
169
+ <div class="bg-blue-50 p-4 rounded-xl border border-blue-100 info-card">
170
+ <p class="text-xs text-blue-600 font-medium">Initial Rate (qi)</p>
171
+ <p id="qiValue" class="text-2xl font-bold text-blue-800">-</p>
172
+ </div>
173
+ <div class="bg-green-50 p-4 rounded-xl border border-green-100 info-card">
174
+ <p class="text-xs text-green-600 font-medium">Decline Rate (Di)</p>
175
+ <p id="diValue" class="text-2xl font-bold text-green-800">-</p>
176
+ </div>
177
+ <div class="bg-purple-50 p-4 rounded-xl border border-purple-100 info-card">
178
+ <p class="text-xs text-purple-600 font-medium">Decline Exponent (b)</p>
179
+ <p id="bValue" class="text-2xl font-bold text-purple-800">-</p>
180
+ </div>
181
+ <div class="bg-orange-50 p-4 rounded-xl border border-orange-100 info-card">
182
+ <p class="text-xs text-orange-600 font-medium">R² Fit</p>
183
+ <p id="r2Value" class="text-2xl font-bold text-orange-800">-</p>
184
+ </div>
185
+ <div class="bg-teal-50 p-4 rounded-xl border border-teal-100 info-card">
186
+ <p class="text-xs text-teal-600 font-medium">Production Life</p>
187
+ <p id="productionLife" class="text-2xl font-bold text-teal-800">-</p>
188
+ </div>
189
+ </div>
190
+
191
+ <!-- Current Performance Section -->
192
+ <div class="bg-gradient-to-r from-blue-50 to-indigo-50 p-6 rounded-xl border border-blue-100 mb-8">
193
+ <h3 class="text-lg font-semibold text-blue-800 mb-4 flex items-center">
194
+ <i class="fas fa-fire mr-2"></i> Current Performance
195
+ </h3>
196
+ <div class="grid grid-cols-1 md:grid-cols-3 gap-6">
197
+ <div class="text-center">
198
+ <p class="text-sm text-gray-600 mb-1">Current Month</p>
199
+ <p id="currentMonth" class="text-2xl font-bold text-gray-800">-</p>
200
+ </div>
201
+ <div class="text-center">
202
+ <p class="text-sm text-gray-600 mb-1">Current Production</p>
203
+ <p id="currentProduction" class="text-2xl font-bold text-green-600">-</p>
204
+ </div>
205
+ <div class="text-center">
206
+ <p class="text-sm text-gray-600 mb-1">Cumulative Production</p>
207
+ <p id="cumulativeProduction" class="text-2xl font-bold text-purple-600">-</p>
208
+ </div>
209
+ </div>
210
+ </div>
211
+
212
+ <!-- Chart -->
213
+ <div class="mb-6">
214
+ <div class="flex justify-between items-center mb-4">
215
+ <h3 class="text-xl font-semibold text-gray-800">Production Rate vs Time</h3>
216
+ <div class="flex space-x-2">
217
+ <select id="declineModel" class="px-3 py-2 border border-gray-300 rounded-lg text-sm focus:outline-none focus:ring-2 focus:ring-blue-500">
218
+ <option value="exponential">Exponential</option>
219
+ <option value="hyperbolic" selected>Hyperbolic</option>
220
+ <option value="harmonic">Harmonic</option>
221
+ </select>
222
+ <input type="number" id="forecastMonths" class="w-20 px-3 py-2 border border-gray-300 rounded-lg text-sm text-center" value="24" min="1" max="120">
223
+ <span class="self-center text-sm text-gray-600">months</span>
224
+ </div>
225
+ </div>
226
+ <div class="chart-container">
227
+ <canvas id="productionChart"></canvas>
228
+ </div>
229
+ </div>
230
+
231
+ <!-- Data Table -->
232
+ <div class="overflow-x-auto mt-8">
233
+ <table id="dataTable" class="min-w-full divide-y divide-gray-200">
234
+ <thead class="bg-gray-50">
235
+ <tr>
236
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Date</th>
237
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Rate (BOPD)</th>
238
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Cumulative (STB)</th>
239
+ <th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Time (months)</th>
240
+ </tr>
241
+ </thead>
242
+ <tbody id="tableBody" class="bg-white divide-y divide-gray-200">
243
+ <!-- Data will be inserted here -->
244
+ </tbody>
245
+ </table>
246
+ </div>
247
+
248
+ <!-- Export button -->
249
+ <div class="text-right mt-6">
250
+ <button id="exportBtn" class="px-4 py-2 bg-gray-600 text-white text-sm font-medium rounded-lg hover:bg-gray-700 transition duration-200">
251
+ <i class="fas fa-download mr-2"></i>Export Results
252
+ </button>
253
+ </div>
254
+ </div>
255
+ </div>
256
+
257
+ <script>
258
+ // Global variables
259
+ let productionData = [];
260
+ let productionChart = null;
261
+ let currentAnalysis = null;
262
+
263
+ // DOM Elements
264
+ const dropZone = document.getElementById('dropZone');
265
+ const fileInput = document.getElementById('fileInput');
266
+ const uploadBtn = document.getElementById('uploadBtn');
267
+ const resultsSection = document.getElementById('resultsSection');
268
+ const declineModel = document.getElementById('declineModel');
269
+ const forecastMonths = document.getElementById('forecastMonths');
270
+ const exportBtn = document.getElementById('exportBtn');
271
+
272
+ // Current well information input elements
273
+ const currentMonthInput = document.getElementById('currentMonthInput');
274
+ const currentProductionInput = document.getElementById('currentProductionInput');
275
+
276
+ // Current performance elements
277
+ const currentMonthEl = document.getElementById('currentMonth');
278
+ const currentProductionEl = document.getElementById('currentProduction');
279
+ const cumulativeProductionEl = document.getElementById('cumulativeProduction');
280
+ const productionLifeEl = document.getElementById('productionLife');
281
+
282
+ // Event Listeners
283
+ dropZone.addEventListener('click', () => fileInput.click());
284
+ dropZone.addEventListener('dragover', handleDragOver);
285
+ dropZone.addEventListener('dragleave', handleDragLeave);
286
+ dropZone.addEventListener('drop', handleDrop);
287
+ fileInput.addEventListener('change', handleFileSelect);
288
+ uploadBtn.addEventListener('click', processFile);
289
+ declineModel.addEventListener('change', updateChart);
290
+ forecastMonths.addEventListener('change', updateChart);
291
+ exportBtn.addEventListener('click', exportResults);
292
+
293
+ // Update the current performance display when manual inputs change
294
+ currentMonthInput.addEventListener('change', updateManualCurrentPerformance);
295
+ currentProductionInput.addEventListener('input', updateManualCurrentPerformance);
296
+
297
+ // Initialize with current date as default
298
+ document.addEventListener('DOMContentLoaded', function() {
299
+ const now = new Date();
300
+ const dateString = now.toLocaleDateString('default', { month: 'short', year: 'numeric' });
301
+ currentMonthEl.textContent = dateString;
302
+ });
303
+
304
+ // Update current performance display with manual inputs
305
+ function updateManualCurrentPerformance() {
306
+ // Update current month
307
+ const monthsAgo = parseInt(currentMonthInput.value) || 0;
308
+ const now = new Date();
309
+ now.setMonth(now.getMonth() - monthsAgo);
310
+ const dateString = now.toLocaleDateString('default', { month: 'short', year: 'numeric' });
311
+ currentMonthEl.textContent = dateString;
312
+
313
+ // Update current production
314
+ const production = parseFloat(currentProductionInput.value);
315
+ if (!isNaN(production) && production >= 0) {
316
+ currentProductionEl.textContent = `${production.toFixed(2)} BOPD`;
317
+ } else {
318
+ currentProductionEl.textContent = "-";
319
+ }
320
+ }
321
+
322
+ function handleDragOver(e) {
323
+ e.preventDefault();
324
+ dropZone.classList.add('highlight');
325
+ }
326
+
327
+ function handleDragLeave(e) {
328
+ e.preventDefault();
329
+ dropZone.classList.remove('highlight');
330
+ }
331
+
332
+ function handleDrop(e) {
333
+ e.preventDefault();
334
+ dropZone.classList.remove('highlight');
335
+
336
+ const files = e.dataTransfer.files;
337
+ if (files.length) {
338
+ fileInput.files = files;
339
+ handleFileSelect({ target: fileInput });
340
+ }
341
+ }
342
+
343
+ function handleFileSelect(e) {
344
+ const file = e.target.files[0];
345
+ if (file) {
346
+ const fileName = file.name;
347
+ if (fileName.endsWith('.xlsx') || fileName.endsWith('.xls')) {
348
+ uploadBtn.disabled = false;
349
+ dropZone.innerHTML = `
350
+ <i class="fas fa-file-excel text-5xl text-green-500 mb-4"></i>
351
+ <h3 class="text-xl font-medium text-gray-700 mb-2">${file.name}</h3>
352
+ <p class="text-sm text-gray-500">Ready to process</p>
353
+ `;
354
+ } else {
355
+ alert('Please upload a valid Excel file (.xlsx or .xls)');
356
+ uploadBtn.disabled = true;
357
+ }
358
+ }
359
+ }
360
+
361
+ async function processFile() {
362
+ const file = fileInput.files[0];
363
+ if (!file) return;
364
+
365
+ const reader = new FileReader();
366
+ reader.onload = function(e) {
367
+ try {
368
+ const data = new Uint8Array(e.target.result);
369
+ const workbook = XLSX.read(data, { type: 'array' });
370
+ const firstSheet = workbook.Sheets[workbook.SheetNames[0]];
371
+
372
+ // Convert to JSON
373
+ const jsonData = XLSX.utils.sheet_to_json(firstSheet);
374
+
375
+ // Parse and validate data
376
+ productionData = parseProductionData(jsonData);
377
+
378
+ if (productionData.length === 0) {
379
+ alert('No valid production data found. Please check your Excel file format.');
380
+ return;
381
+ }
382
+
383
+ // Sort by date
384
+ productionData.sort((a, b) => a.timeMonths - b.timeMonths);
385
+
386
+ // Make sure current performance info is updated
387
+ if (currentMonthInput.value || currentProductionInput.value) {
388
+ updateManualCurrentPerformance();
389
+ }
390
+
391
+ // Display results
392
+ displayResults();
393
+
394
+ // Analyze decline curve
395
+ currentAnalysis = performDeclineAnalysis(productionData);
396
+
397
+ // Update analysis info
398
+ updateAnalysisInfo(currentAnalysis);
399
+
400
+ // Update current performance - this will prioritize data from Excel if available
401
+ updateCurrentPerformance();
402
+
403
+ // Create chart
404
+ createChart(productionData, currentAnalysis);
405
+
406
+ // Show results section
407
+ resultsSection.classList.remove('hidden');
408
+
409
+ // Scroll to results
410
+ resultsSection.scrollIntoView({ behavior: 'smooth' });
411
+
412
+ } catch (error) {
413
+ console.error('Error processing file:', error);
414
+ alert('Error processing the Excel file. Please make sure it is a valid file.');
415
+ }
416
+ };
417
+
418
+ reader.readAsArrayBuffer(file);
419
+ }
420
+
421
+ function parseProductionData(jsonData) {
422
+ const parsedData = [];
423
+ let startDate = null;
424
+
425
+ for (let row of jsonData) {
426
+ let date, rate, cumulative;
427
+
428
+ // Try to detect columns by common names
429
+ for (let key in row) {
430
+ const keyLower = key.toLowerCase();
431
+ if (date === undefined && (keyLower.includes('date') || keyLower.includes('time'))) {
432
+ date = new Date(row[key]);
433
+ }
434
+ if (rate === undefined && (keyLower.includes('rate') || keyLower.includes('oil') || keyLower.includes('prod'))) {
435
+ rate = parseFloat(row[key]);
436
+ }
437
+ if (cumulative === undefined && (keyLower.includes('cum') || keyLower.includes('total') || keyLower.includes('cumulative'))) {
438
+ cumulative = parseFloat(row[key]);
439
+ }
440
+ }
441
+
442
+ // Fallback: assume first three columns are date, rate, cumulative if no headers
443
+ if (parsedData.length === 0 && Object.keys(row).length === 3 && date === undefined) {
444
+ const values = Object.values(row);
445
+ date = new Date(values[0]);
446
+ rate = parseFloat(values[1]);
447
+ cumulative = parseFloat(values[2]);
448
+ }
449
+
450
+ // Validate data
451
+ if (date && !isNaN(date.getTime()) && !isNaN(rate) && rate >= 0) {
452
+ // Set start date for time calculations
453
+ if (!startDate) {
454
+ startDate = new Date(date);
455
+ }
456
+
457
+ // Calculate time in months from start
458
+ const timeMonths = (date - startDate) / (1000 * 60 * 60 * 24 * 30.44);
459
+
460
+ parsedData.push({
461
+ date: new Date(date),
462
+ rate: rate,
463
+ cumulative: !isNaN(cumulative) ? cumulative : null,
464
+ timeMonths: parseFloat(timeMonths.toFixed(2))
465
+ });
466
+ }
467
+ }
468
+
469
+ return parsedData;
470
+ }
471
+
472
+ function performDeclineAnalysis(data) {
473
+ // Simple decline curve analysis using hyperbolic decline
474
+ // q(t) = qi / (1 + b * Di * t)^(1/b)
475
+
476
+ const qi = data[0].rate; // Initial rate
477
+ const productionMonths = data[data.length - 1].timeMonths - data[0].timeMonths;
478
+
479
+ // Simple estimation of decline parameters
480
+ // This is a basic implementation - in reality, you'd use non-linear regression
481
+ let Di = 0; // Nominal decline rate
482
+ let b = 0.5; // Decline exponent (b=0 exponential, b=1 harmonic, 0<b<1 hyperbolic)
483
+ let rSquared = 0;
484
+
485
+ // Calculate nominal decline rate based on first and last points
486
+ if (data.length > 1 && productionMonths > 0) {
487
+ const qf = data[data.length - 1].rate;
488
+
489
+ // For hyperbolic decline, we'll make a simple estimation
490
+ const avgDecline = (qi - qf) / qi / productionMonths;
491
+ Di = avgDecline * 12; // Annual decline rate
492
+
493
+ // Adjust b factor based on the shape of the decline
494
+ // This is very simplified - real analysis would use proper regression
495
+ if (qf / qi > 0.5) {
496
+ b = 0.3; // Steeper decline
497
+ } else if (qf / qi > 0.3) {
498
+ b = 0.5;
499
+ } else {
500
+ b = 0.8; // Gentle decline
501
+ }
502
+ } else {
503
+ Di = 0.2; // Default 20% annual decline
504
+ b = 0.5;
505
+ }
506
+
507
+ // Calculate R-squared (very simplified)
508
+ let ssRes = 0;
509
+ let ssTot = 0;
510
+ const yMean = data.reduce((sum, point) => sum + point.rate, 0) / data.length;
511
+
512
+ data.forEach(point => {
513
+ const predicted = predictRate(point.timeMonths, qi, Di, b);
514
+ ssRes += Math.pow(point.rate - predicted, 2);
515
+ ssTot += Math.pow(point.rate - yMean, 2);
516
+ });
517
+
518
+ rSquared = 1 - (ssRes / Math.max(ssTot, Number.EPSILON));
519
+
520
+ return { qi, Di, b, rSquared };
521
+ }
522
+
523
+ function predictRate(time, qi, Di, b) {
524
+ // Hyperbolic decline model
525
+ // q(t) = qi / (1 + b * Di * t)^(1/b)
526
+ // Time is in years for standard decline parameters
527
+ const timeYears = time / 12;
528
+
529
+ if (b === 0) {
530
+ // Exponential decline
531
+ return qi * Math.exp(-Di * timeYears);
532
+ } else if (Math.abs(b - 1) < 0.01) {
533
+ // Harmonic decline
534
+ return qi / (1 + Di * timeYears);
535
+ } else {
536
+ // Hyperbolic decline
537
+ const base = 1 + b * Di * timeYears;
538
+ return base > 0 ? qi / Math.pow(base, 1/b) : 0;
539
+ }
540
+ }
541
+
542
+ function updateCurrentPerformance() {
543
+ if (productionData.length === 0) return;
544
+
545
+ // Get the latest data point from Excel
546
+ const latestPoint = productionData[productionData.length - 1];
547
+
548
+ // Update current month
549
+ currentMonthEl.textContent = latestPoint.date.toLocaleDateString('default', { month: 'short', year: 'numeric' });
550
+
551
+ // Update current production
552
+ currentProductionEl.textContent = `${latestPoint.rate.toFixed(2)} BOPD`;
553
+
554
+ // Update cumulative production
555
+ if (latestPoint.cumulative) {
556
+ cumulativeProductionEl.textContent = `${Math.round(latestPoint.cumulative).toLocaleString()} STB`;
557
+ } else {
558
+ // If cumulative values aren't provided, estimate based on trapezoidal rule
559
+ let estimatedCumulative = 0;
560
+ for (let i = 1; i < productionData.length; i++) {
561
+ const dt = (productionData[i].timeMonths - productionData[i-1].timeMonths) * 30.44; // days
562
+ const avgRate = (productionData[i].rate + productionData[i-1].rate) / 2;
563
+ estimatedCumulative += avgRate * dt;
564
+ }
565
+ cumulativeProductionEl.textContent = `${Math.round(estimatedCumulative).toLocaleString()} STB (estimated)`;
566
+ }
567
+
568
+ // Calculate and display production life
569
+ const productionLifeInMonths = latestPoint.timeMonths;
570
+ const years = Math.floor(productionLifeInMonths / 12);
571
+ const months = Math.round(productionLifeInMonths % 12);
572
+
573
+ if (years > 0) {
574
+ productionLifeEl.textContent = `${years}y ${months}m`;
575
+ } else {
576
+ productionLifeEl.textContent = `${months} months`;
577
+ }
578
+ }
579
+
580
+ function displayResults() {
581
+ // Populate table
582
+ const tableBody = document.getElementById('tableBody');
583
+ tableBody.innerHTML = '';
584
+
585
+ productionData.forEach(point => {
586
+ const row = document.createElement('tr');
587
+ row.innerHTML = `
588
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-700">${point.date.toLocaleDateString()}</td>
589
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-700">${point.rate.toFixed(2)}</td>
590
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-700">${point.cumulative ? point.cumulative.toFixed(0) : '-'}</td>
591
+ <td class="px-6 py-4 whitespace-nowrap text-sm text-gray-700">${point.timeMonths.toFixed(1)}</td>
592
+ `;
593
+ tableBody.appendChild(row);
594
+ });
595
+ }
596
+
597
+ function updateAnalysisInfo(analysis) {
598
+ document.getElementById('qiValue').textContent = `${analysis.qi.toFixed(2)} BOPD`;
599
+ document.getElementById('diValue').textContent = `${(analysis.Di * 100).toFixed(1)}%/yr`;
600
+ document.getElementById('bValue').textContent = analysis.b.toFixed(2);
601
+ document.getElementById('r2Value').textContent = analysis.rSquared.toFixed(3);
602
+ }
603
+
604
+ function createChart(data, analysis) {
605
+ const ctx = document.getElementById('productionChart').getContext('2d');
606
+
607
+ // Destroy existing chart if it exists
608
+ if (productionChart) {
609
+ productionChart.destroy();
610
+ }
611
+
612
+ const actualDates = data.map(d => d.timeMonths);
613
+ const actualRates = data.map(d => d.rate);
614
+
615
+ // Generate forecast data
616
+ const maxTime = Math.max(...data.map(d => d.timeMonths));
617
+ const forecastTime = [];
618
+ const forecastRates = [];
619
+
620
+ const forecastPeriod = parseInt(forecastMonths.value);
621
+ const model = declineModel.value;
622
+
623
+ const b = model === 'exponential' ? 0 : (model === 'harmonic' ? 1 : analysis.b);
624
+
625
+ // Create data points for the chart
626
+ for (let t = 0; t <= maxTime + forecastPeriod; t += 0.5) {
627
+ forecastTime.push(t);
628
+ forecastRates.push(predictRate(t, analysis.qi, analysis.Di, b));
629
+ }
630
+
631
+ productionChart = new Chart(ctx, {
632
+ type: 'line',
633
+ data: {
634
+ labels: forecastTime,
635
+ datasets: [
636
+ {
637
+ label: 'Historical Production',
638
+ data: actualRates,
639
+ borderColor: '#4c1d95',
640
+ backgroundColor: 'rgba(76, 29, 149, 0.1)',
641
+ borderWidth: 3,
642
+ pointBackgroundColor: '#4c1d95',
643
+ pointRadius: 4,
644
+ pointHoverRadius: 6,
645
+ fill: false,
646
+ tension: 0.2
647
+ },
648
+ {
649
+ label: 'Forecasted Decline',
650
+ data: forecastRates,
651
+ borderColor: '#059669',
652
+ borderDash: [10, 5],
653
+ borderWidth: 3,
654
+ pointBackgroundColor: '#059669',
655
+ pointRadius: 0,
656
+ fill: false,
657
+ tension: 0.2
658
+ }
659
+ ]
660
+ },
661
+ options: {
662
+ responsive: true,
663
+ maintainAspectRatio: false,
664
+ interaction: {
665
+ mode: 'index',
666
+ intersect: false
667
+ },
668
+ plugins: {
669
+ tooltip: {
670
+ backgroundColor: 'rgba(0, 0, 0, 0.8)',
671
+ titleColor: '#ffffff',
672
+ bodyColor: '#ffffff',
673
+ borderColor: '#ffffff',
674
+ borderWidth: 1,
675
+ cornerRadius: 8,
676
+ displayColors: true,
677
+ callbacks: {
678
+ title: function(tooltipItems) {
679
+ const timeMonths = tooltipItems[0].label;
680
+ const years = Math.floor(timeMonths / 12);
681
+ const months = Math.round(timeMonths % 12);
682
+ return `Time: ${years}y ${months}m`;
683
+ },
684
+ label: function(context) {
685
+ return `${context.dataset.label}: ${context.parsed.y.toFixed(2)} BOPD`;
686
+ }
687
+ }
688
+ },
689
+ legend: {
690
+ position: 'top',
691
+ labels: {
692
+ usePointStyle: true,
693
+ padding: 20,
694
+ font: {
695
+ size: 14,
696
+ weight: 500
697
+ }
698
+ }
699
+ },
700
+ title: {
701
+ display: false
702
+ }
703
+ },
704
+ scales: {
705
+ x: {
706
+ type: 'linear',
707
+ position: 'bottom',
708
+ title: {
709
+ display: true,
710
+ text: 'Time (months)',
711
+ font: {
712
+ size: 14,
713
+ weight: 500
714
+ }
715
+ },
716
+ grid: {
717
+ color: 'rgba(0, 0, 0, 0.05)'
718
+ }
719
+ },
720
+ y: {
721
+ title: {
722
+ display: true,
723
+ text: 'Production Rate (BOPD)',
724
+ font: {
725
+ size: 14,
726
+ weight: 500
727
+ }
728
+ },
729
+ grid: {
730
+ color: 'rgba(0, 0, 0, 0.05)'
731
+ },
732
+ beginAtZero: true
733
+ }
734
+ }
735
+ }
736
+ });
737
+ }
738
+
739
+ function updateChart() {
740
+ if (productionData.length > 0 && currentAnalysis) {
741
+ createChart(productionData, currentAnalysis);
742
+ }
743
+ }
744
+
745
+ function exportResults() {
746
+ // In case the user didn't update the manual inputs, make sure current performance is set
747
+ if (!currentProductionEl.textContent || currentProductionEl.textContent === "-") {
748
+ updateManualCurrentPerformance();
749
+ }
750
+
751
+ // Create a worksheet
752
+ const ws_data = [
753
+ ['Date', 'Rate (BOPD)', 'Cumulative (STB)', 'Time (months)'],
754
+ ...productionData.map(d => [d.date.toLocaleDateString(), d.rate, d.cumulative, d.timeMonths])
755
+ ];
756
+
757
+ const ws = XLSX.utils.aoa_to_sheet(ws_data);
758
+
759
+ // Add analysis results
760
+ XLSX.utils.sheet_add_aoa(ws, [
761
+ ['', ''], // blank row
762
+ ['Analysis Results', ''],
763
+ ['Initial Rate (qi)', document.getElementById('qiValue').textContent],
764
+ ['Decline Rate (Di)', document.getElementById('diValue').textContent],
765
+ ['Decline Exponent (b)', document.getElementById('bValue').textContent],
766
+ ['R² Fit', document.getElementById('r2Value').textContent],
767
+ ['Production Life', document.getElementById('productionLife').textContent],
768
+ ['Current Month', currentMonthEl.textContent],
769
+ ['Current Production', currentProductionEl.textContent],
770
+ ['Cumulative Production', cumulativeProductionEl.textContent],
771
+ ['Model Used', declineModel.options[declineModel.selectedIndex].text],
772
+ ['Forecast Period', `${forecastMonths.value} months`]
773
+ ], { origin: -1 });
774
+
775
+ // Create workbook and download
776
+ const wb = XLSX.utils.book_new();
777
+ XLSX.utils.book_append_sheet(wb, ws, 'Production Data');
778
+
779
+ // Generate file name
780
+ const dateStr = new Date().toISOString().slice(0, 10);
781
+ XLSX.writeFile(wb, `DeclineCurveAnalysis_${dateStr}.xlsx`);
782
+ }
783
+ </script>
784
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-qwensite.hf.space/logo.svg" alt="qwensite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-qwensite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >QwenSite</a> - 🧬 <a href="https://enzostvs-qwensite.hf.space?remix=alterzick/dca" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
785
+ </html>
prompts.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ tolong buatkan decline curve analisis untuk produksi sumur dengan melakukan upload excel dan di tampilkan dalam grafik
2
+ tolong tambahkan informasi untuk bulan dan produksi saat ini
3
+ tolong tambahkan input manual untuk bulan dan produksi saat ini sebagai informasi tambahan sebelum upload excel