File size: 13,251 Bytes
69c8022
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
09e56d5
69c8022
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
bb1fd7f
69c8022
 
 
 
 
 
 
 
 
 
 
 
56aac82
69c8022
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
import gradio as gr

# ------------------------------------
# 1. DEFINE THE STVI THRESHOLDS
# ------------------------------------
STVI_THRESHOLDS = {
    "N": {
        "Very Low":  (0.00, 0.09),
        "Low":       (0.091, 0.18),
        "Medium":    (0.181, 0.27),
        "Optimum":   (0.271, 0.36),
        "High":      (0.361, 0.45),
        "Very High": (0.451, 999.999),
    },
    "P": {
        # Olsen method (µg/g)
        "Very Low":  (0.00, 6.0),
        "Low":       (6.1, 12.0),
        "Medium":    (12.1, 18.0),
        "Optimum":   (18.1, 24.0),
        "High":      (24.1, 30.0),
        "Very High": (30.1, 999.999),
    },
    "K": {
        # meq/100g
        "Very Low":  (0.00, 0.075),
        "Low":       (0.076, 0.15),
        "Medium":    (0.151, 0.225),
        "Optimum":   (0.226, 0.30),
        "High":      (0.31, 0.375),
        "Very High": (0.376, 999.999),
    },
    "S": {
        # µg/g
        "Very Low":  (0.00, 9.0),
        "Low":       (9.1, 18.0),
        "Medium":    (18.1, 27.0),
        "Optimum":   (27.1, 36.0),
        "High":      (36.1, 45.0),
        "Very High": (45.1, 999.999),
    },
    "Zn": {
        # µg/g
        "Very Low":  (0.00, 0.45),
        "Low":       (0.451, 0.90),
        "Medium":    (0.91, 1.35),
        "Optimum":   (1.351, 1.80),
        "High":      (1.81, 2.25),
        "Very High": (2.251, 999.999),
    },
    "B": {
        # µg/g, as per your table
        "Very Low":  (0.00, 0.15),
        "Low":       (0.151, 0.30),
        "Medium":    (0.31, 0.45),
        "Optimum":   (0.451, 0.60),
        "High":      (0.61, 0.75),
        "Very High": (0.751, 999.999),
    },
}

# ------------------------------------
# 2. RECOMMENDED RANGES PER VARIETY/YIELD

aman_rice = {
    "N": {
        "Optimum":   (0, 12),
        "Medium":    (13, 24),
        "Low":       (25, 36),
        "Very Low":  (37, 48),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
    "P": {
        "Optimum":   (0, 3),
        "Medium":    (4, 6),
        "Low":       (7, 9),
        "Very Low":  (10, 12),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
    "K": {
        "Optimum":   (0, 10),
        "Medium":    (11, 20),
        "Low":       (21, 30),
        "Very Low":  (31, 40),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
    "S": {
        "Optimum":   (0, 2),
        "Medium":    (3, 4),
        "Low":       (5, 6),
        "Very Low":  (7, 8),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
    "Zn": {
        "Optimum":   (0, 0),
        "Medium":    (0, 0.5),
        "Low":       (0.6, 1.0),
        "Very Low":  (1.1, 1.5),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
    "B": {
        "Optimum":   (0, 0),
        "Medium":    (0, 0.5),
        "Low":       (0.6, 1.0),
        "Very Low":  (1.1, 1.5),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
}

big_list_5_0 = {
    "N": {
        "Optimum":   (0, 30),
        "Medium":    (31, 60),
        "Low":       (61, 90),
        "Very Low":  (91, 120),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
    "P": {
        "Optimum":   (0, 5),
        "Medium":    (6, 10),
        "Low":       (11, 15),
        "Very Low":  (16, 20),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
    "K": {
        "Optimum":   (0, 25),
        "Medium":    (26, 50),
        "Low":       (51, 75),
        "Very Low":  (76, 100),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
    "S": {
        "Optimum":   (0, 4),
        "Medium":    (5, 8),
        "Low":       (9, 12),
        "Very Low":  (13, 16),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
    "Zn": {
        "Optimum":   (0, 0),
        "Medium":    (0, 0.8),
        "Low":       (0.9, 1.6),
        "Very Low":  (1.7, 2.4),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
    "B": {
        "Optimum":   (0, 0),
        "Medium":    (0, 0.8),
        "Low":       (0.9, 1.6),
        "Very Low":  (1.7, 2.4),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
}

big_list_4_0 = {
    "N": {
        "Optimum":   (0, 24),
        "Medium":    (25, 48),
        "Low":       (49, 72),
        "Very Low":  (73, 96),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
    "P": {
        "Optimum":   (0, 4),
        "Medium":    (5, 8),
        "Low":       (9, 12),
        "Very Low":  (13, 16),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
    "K": {
        "Optimum":   (0, 20),
        "Medium":    (21, 40),
        "Low":       (41, 60),
        "Very Low":  (61, 80),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
    "S": {
        "Optimum":   (0, 3),
        "Medium":    (4, 6),
        "Low":       (7, 9),
        "Very Low":  (10, 12),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
    "Zn": {
        "Optimum":   (0, 0),
        "Medium":    (0, 0.7),
        "Low":       (0.8, 1.4),
        "Very Low":  (1.5, 2.1),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
    "B": {
        "Optimum":   (0, 0),
        "Medium":    (0, 0.7),
        "Low":       (0.8, 1.4),
        "Very Low":  (1.5, 2.1),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
}

big_list_3_0 = {
    "N": {
        "Optimum":   (0, 18),
        "Medium":    (19, 36),
        "Low":       (37, 54),
        "Very Low":  (55, 72),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
    "P": {
        "Optimum":   (0, 3),
        "Medium":    (4, 6),
        "Low":       (7, 9),
        "Very Low":  (10, 12),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
    "K": {
        "Optimum":   (0, 15),
        "Medium":    (16, 30),
        "Low":       (31, 45),
        "Very Low":  (46, 60),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
    "S": {
        "Optimum":   (0, 3),
        "Medium":    (4, 6),
        "Low":       (7, 9),
        "Very Low":  (10, 12),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
    "Zn": {
        "Optimum":   (0, 0),
        "Medium":    (0, 0.6),
        "Low":       (0.7, 1.2),
        "Very Low":  (1.3, 1.8),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
    "B": {
        "Optimum":   (0, 0),
        "Medium":    (0, 0.6),
        "Low":       (0.7, 1.2),
        "Very Low":  (1.3, 1.8),
        "High":      (0, 0),
        "Very High": (0, 0),
    },
}

VARIETY_OPTIONS = {
    "Aman Rice": aman_rice,
    "BR 11, BR 22, BR 23, BRRI dhan40, BRRI dhan41, BRRI dhan44, BRRI dhan46, BRRI dhan49,\n"
    "BRRI dhan51, BRRI dhan52, BRRI dhan53, BRRI dhan54, BRRI dhan56, BRRI dhan62,\n"
    "BRRI dhan66, BRRI dhan70, BRRI dhan71, BRRI dhan72, BRRI dhan73, BRRI dhan75\n"
    "BRRI dhan76, BRRI dhan78, BRRI dhan79, BRRI dhan80, BRRI hybrid dhan4, BRRI hybrid dhan6\n"
    "and Binadhan-4, Binadhan-7, Binadhan-11, Binadhan-12, Binadhan-15, Binadhan-16, Binadhan-17, Binadhan-20": big_list_5_0,
    "BR25, BRRI dhan33, BRRI dhan34, BRRI dhan37, BRRI dhan38,\nBRRI dhan39, BRRI dhan56, BRRI dhan57 and Binadhan-12, Binadhan-13": big_list_4_0,
    "BR5, Binadhan-9; LIV: Kataribhog, Kalijira, Chinigura etc": big_list_3_0,
}

# ------------------------------------
# 3. FERTILIZER CONVERSIONS
# ------------------------------------
FERTILIZER_CONVERSION = {
    "N":  {
        "product": "Urea",
        "ratio":  2.17,   # e.g., 1 kg N => ~2.17 kg Urea
    },
    "P":  {
        "product": "TSP",
        "ratio":  5.0,    # e.g., 1 kg P => ~5 kg TSP
    },
    "K":  {
        "product": "MoP",
        "ratio":  2.0,    # e.g., 1 kg K => 2 kg MOP
    },
    "S":  {
        "product": "Gypsum",
        "ratio":  5.55,   # e.g., 1 kg S => ~5.55 kg gypsum
    },
    "Zn": {
        "product": "Zinc sulphate (heptahydrate)",
        "ratio":  4.75,
    },
    "B": {
        "product": "Boric acid",
        "ratio":  5.88,   # e.g., 1 kg B => 5.88 kg boric acid
    },
}


def classify_soil_test(nutrient, value):
    """
    Determine the STVI class (Very Low, Low, Medium, etc.) for a given nutrient
    based on the user-supplied soil test 'value'.
    """
    if nutrient not in STVI_THRESHOLDS:
        return None

    thresholds = STVI_THRESHOLDS[nutrient]
    for stvi_class, (lo, hi) in thresholds.items():
        if lo <= value <= hi:
            return stvi_class
    return None

def calculate_F_r(Uf, Ci, Cs, St, Ls):
    """
    Fertilizer requirement formula:
      F_r = U_f - (C_i / C_s) * (S_t - L_s)
    """
    if Cs == 0:  # Avoid division by zero
        return Uf
    return Uf - (Ci / Cs) * (St - Ls)  # Corrected variable name to Ls

# ------------------------------------
# 5. MAIN CALCULATION LOGIC
# ------------------------------------
def fertilizer_calculator(
    soilN, soilP, soilK, soilS, soilZn, soilB,
    variety_choice
):
   
    rec_dict = VARIETY_OPTIONS[variety_choice]
    results = []

    def process_nutrient(nutrient_name, soil_value):
        if soil_value is None:
            return None  # user left it blank => skip

        stvi_class = classify_soil_test(nutrient_name, soil_value)
        if stvi_class is None:
            return f"{nutrient_name}: Soil test value {soil_value} out of range or no data."

        if nutrient_name not in rec_dict:
            return f"{nutrient_name}: No recommendation data for {variety_choice}."
        if stvi_class not in rec_dict[nutrient_name]:
            return f"{nutrient_name}: No recommended range for STVI class '{stvi_class}'."

        (range_lo, range_hi) = rec_dict[nutrient_name][stvi_class]
        # U_f = the upper limit of that recommended range
        Uf = range_hi
        Ci = range_hi - range_lo

        # The STVI boundaries for that class
        (class_lo, class_hi) = STVI_THRESHOLDS[nutrient_name][stvi_class]
        Ls = class_lo
        Cs = class_hi - class_lo
        St = soil_value

        # Calculate F_r
        Fr = calculate_F_r(Uf, Ci, Cs, St, Ls)
        Fr = max(0, Fr)  # Ensure no negative

        if nutrient_name in FERTILIZER_CONVERSION:
            fert_prod = FERTILIZER_CONVERSION[nutrient_name]["product"]
            ratio = FERTILIZER_CONVERSION[nutrient_name]["ratio"]
            fert_amount = Fr * ratio
            return (
                f"{nutrient_name} - STVI: {stvi_class} | "
                f"Recommended Nutrient = {Fr:.2f} kg/ha | "
                f"{fert_prod} needed ≈ {fert_amount:.2f} kg/ha"
            )
        else:
            return (
                f"{nutrient_name} - STVI: {stvi_class} | "
                f"Recommended Nutrient = {Fr:.2f} kg/ha | "
                f"No fertilizer product data."
            )

    # Process each nutrient in turn
    for nname, sval in [
        ("N",  soilN),
        ("P",  soilP),
        ("K",  soilK),
        ("S",  soilS),
        ("Zn", soilZn),
        ("B",  soilB),
    ]:
        ans = process_nutrient(nname, sval)
        if ans:
            results.append(ans)

    if not results:
        return "No valid nutrient inputs given."
    return "\n".join(results)

# ------------------------------------
# GRADIO UI
# ------------------------------------
import gradio as gr
import traceback


def on_calculate(soilN, soilP, soilK, soilS, soilZn, soilB, variety_choice):
    try:
        # Attempt the fertilizer calculation
        return fertilizer_calculator(soilN, soilP, soilK, soilS, soilZn, soilB, variety_choice)
    except Exception as e:
        # Catch any errors and return the stack trace to the UI
        error_message = f"An error occurred:\n{traceback.format_exc()}"
        print(error_message)  # Print to the console for debugging
        return error_message  # Return to the Gradio output

# Integrate with the Gradio app
with gr.Blocks(title="Fertilizer Calculator 🌾🚜") as demo:
    gr.Markdown(
        """
        # Fertilizer Calculator 🌾🚜  
        Enter your soil test values below (leave blank if unknown).  
        Then select your rice variety to see recommended fertilizer requirements!
        """
    )

    with gr.Row():
        soilN = gr.Number(label="Nitrogen (%)", value=None, precision=4)
        soilP = gr.Number(label="Phosphorus (µg/g, Olsen)", value=None, precision=4)
        soilK = gr.Number(label="Potassium (meq/100g)", value=None, precision=4)
        soilS = gr.Number(label="Sulfur (µg/g)", value=None, precision=4)
        soilZn = gr.Number(label="Zinc (µg/g)", value=None, precision=4)
        soilB = gr.Number(label="Boron (µg/g)", value=None, precision=4)

    variety = gr.Dropdown(
        label="Select Rice Variety",
        choices=list(VARIETY_OPTIONS.keys()),
        value="Aman Rice"  # default
    )

    calc_button = gr.Button("Calculate Fertilizer Requirements ✅")
    output_text = gr.Textbox(
        label="Results",
        lines=12,
        placeholder="Your fertilizer recommendation will appear here..."
    )

    calc_button.click(
        fn=on_calculate,
        inputs=[soilN, soilP, soilK, soilS, soilZn, soilB, variety],
        outputs=output_text
    )

demo.launch()