UPDATE
This commit is contained in:
@@ -0,0 +1,822 @@
|
||||
# راهنمای کامل `crop_simulation/services.py`
|
||||
|
||||
این فایل توضیح میدهد که سرویسهای شبیهسازی در `crop_simulation/services.py` چه کاری انجام میدهند، ورودی و خروجی هر بخش چیست، و چگونه با تنظیمات موجود در `irrigation/apps.py` و `fertilization/apps.py` ارتباط میگیرند.
|
||||
|
||||
---
|
||||
|
||||
## نمای کلی
|
||||
|
||||
فایل `crop_simulation/services.py` هسته اجرای سناریوهای شبیهسازی محصول در پروژه است. این فایل سه مسئولیت اصلی دارد:
|
||||
|
||||
1. نرمالسازی ورودیها برای موتور شبیهسازی
|
||||
2. اجرای مدل PCSE/WOFOST
|
||||
3. ذخیره و مدیریت سناریوها و runها در دیتابیس
|
||||
|
||||
در عمل این فایل بین دادههای خام مزرعه/هواشناسی/مدیریتی و خروجی نهایی شبیهسازی قرار میگیرد.
|
||||
|
||||
---
|
||||
|
||||
## ساختار کلی فایل
|
||||
|
||||
این فایل را میتوان به ۴ بخش تقسیم کرد:
|
||||
|
||||
1. توابع کمکی برای تبدیل ورودیها
|
||||
2. کلاس `PcseSimulationManager`
|
||||
3. کلاس `CropSimulationService`
|
||||
4. wrapperهای سطح ماژول برای استفاده سادهتر
|
||||
|
||||
---
|
||||
|
||||
## بخش اول: ثابتها و Exception
|
||||
|
||||
### `DEFAULT_OUTPUT_VARS`
|
||||
لیست متغیرهایی که از خروجی روزانه مدل میخواهیم:
|
||||
|
||||
- `DVS`
|
||||
- `LAI`
|
||||
- `TAGP`
|
||||
- `TWSO`
|
||||
- `SM`
|
||||
|
||||
### `DEFAULT_SUMMARY_VARS`
|
||||
متغیرهای خلاصه:
|
||||
|
||||
- `TAGP`
|
||||
- `TWSO`
|
||||
- `CTRAT`
|
||||
- `RD`
|
||||
|
||||
### `DEFAULT_TERMINAL_VARS`
|
||||
متغیرهای انتهایی:
|
||||
|
||||
- `TAGP`
|
||||
- `TWSO`
|
||||
- `LAI`
|
||||
- `DVS`
|
||||
|
||||
### `CropSimulationError`
|
||||
خطای اختصاصی این ماژول است. هر جا داده ورودی یا اجرای مدل مشکل داشته باشد، معمولا این exception یا exceptionهای مشتقشده از آن دیده میشود.
|
||||
|
||||
---
|
||||
|
||||
## بخش دوم: توابع کمکی داخلی
|
||||
|
||||
این توابع public API نیستند، اما پایه رفتار کل سرویس را تشکیل میدهند.
|
||||
|
||||
### `_json_ready(value)`
|
||||
دادههای Python را برای ذخیره در JSON آماده میکند.
|
||||
|
||||
کارهایی که انجام میدهد:
|
||||
|
||||
- `dict`، `list` و `tuple` را recursive تبدیل میکند
|
||||
- `date` و `datetime` را به `isoformat()` تبدیل میکند
|
||||
|
||||
موارد استفاده:
|
||||
|
||||
- قبل از ذخیره `input_payload`
|
||||
- قبل از ذخیره `result_payload`
|
||||
- قبل از ذخیره payload هر `SimulationRun`
|
||||
|
||||
### `_coerce_date(value)`
|
||||
ورودی را به `date` تبدیل میکند.
|
||||
|
||||
ورودی قابل قبول:
|
||||
|
||||
- `date`
|
||||
- `datetime`
|
||||
- رشته ISO مثل `2026-04-01`
|
||||
|
||||
اگر نوع پشتیبانی نشود، `CropSimulationError` میدهد.
|
||||
|
||||
### `_normalize_weather_records(weather)`
|
||||
ورودی آبوهوا را به فرمت استاندارد موردنیاز PCSE تبدیل میکند.
|
||||
|
||||
ورودی قابل قبول:
|
||||
|
||||
- یک `dict`
|
||||
- یک `list[dict]`
|
||||
- یک آبجکت با کلید `records`
|
||||
|
||||
خروجی همیشه لیستی از رکوردهای نرمالشده با کلیدهای زیر است:
|
||||
|
||||
- `DAY`
|
||||
- `LAT`
|
||||
- `LON`
|
||||
- `ELEV`
|
||||
- `IRRAD`
|
||||
- `TMIN`
|
||||
- `TMAX`
|
||||
- `VAP`
|
||||
- `WIND`
|
||||
- `RAIN`
|
||||
- `E0`
|
||||
- `ES0`
|
||||
- `ET0`
|
||||
|
||||
اگر رکوردها خالی باشند، خطا میدهد.
|
||||
|
||||
### `_normalize_agromanagement(agromanagement)`
|
||||
ورودی agromanagement را به یک `list[dict]` تبدیل میکند.
|
||||
|
||||
ورودی قابل قبول:
|
||||
|
||||
- دیکشنری با کلید `AgroManagement`
|
||||
- لیست
|
||||
- یک دیکشنری تکی
|
||||
|
||||
اگر خالی باشد، خطا میدهد.
|
||||
|
||||
### `_deep_copy_json_like(value)`
|
||||
نسخه deep copy ساده از objectهای JSON-like میسازد.
|
||||
|
||||
برای جلوگیری از mutation روی ورودی اصلی استفاده میشود.
|
||||
|
||||
### `_parse_recommendation_events(...)`
|
||||
دادههای توصیه آبیاری یا کودهی را به فرمت event قابل الحاق به `TimedEvents` تبدیل میکند.
|
||||
|
||||
این تابع از چند شکل ورودی پشتیبانی میکند:
|
||||
|
||||
- `events`
|
||||
- `schedule`
|
||||
- `applications`
|
||||
- `plan`
|
||||
|
||||
نمونه ورودی آبیاری:
|
||||
|
||||
```python
|
||||
{
|
||||
"events": [
|
||||
{"date": "2026-04-25", "amount": 2.5, "efficiency": 0.8}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
نمونه خروجی:
|
||||
|
||||
```python
|
||||
[
|
||||
{
|
||||
"event_signal": "irrigate",
|
||||
"name": "irrigate recommendation",
|
||||
"comment": "",
|
||||
"events_table": [
|
||||
{
|
||||
date(2026, 4, 25): {"amount": 2.5, "efficiency": 0.8}
|
||||
}
|
||||
],
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### `_merge_management_recommendations(...)`
|
||||
مهمترین تابع glue برای اتصال recommendationها به شبیهسازی است.
|
||||
|
||||
کار این تابع:
|
||||
|
||||
1. agromanagement را normalize میکند
|
||||
2. توصیه آبیاری را به eventهای `irrigate` تبدیل میکند
|
||||
3. توصیه کودهی را به eventهای `apply_n` تبدیل میکند
|
||||
4. همه آنها را داخل اولین campaign معتبر در `TimedEvents` merge میکند
|
||||
|
||||
این تابع همان نقطهای است که recommendationهای اپ آبیاری/کودهی به سناریوی شبیهسازی تزریق میشوند.
|
||||
|
||||
### `_normalize_pcse_output_records(records)`
|
||||
خروجیهای مدل PCSE را به لیست تبدیل میکند تا کدهای بعدی همیشه با ساختار یکنواخت کار کنند.
|
||||
|
||||
### `_pick_first_not_none(*values)`
|
||||
اولین مقدار non-null را برمیگرداند.
|
||||
|
||||
برای ساخت metricهای نهایی مثل `yield_estimate` استفاده میشود.
|
||||
|
||||
### `_extract_total_n(agromanagement)`
|
||||
جمع کل `N_amount` را از eventهای کودهی استخراج میکند.
|
||||
|
||||
در نسخه فعلی این تابع برای محاسبات جانبی آماده است و نقطه مناسبی برای توسعه تحلیل استراتژیهای تغذیه است.
|
||||
|
||||
### `_load_pcse_bindings()`
|
||||
کلاسها و ماژولهای لازم از package `pcse` را load میکند:
|
||||
|
||||
- `ParameterProvider`
|
||||
- `WeatherDataProvider`
|
||||
- `WeatherDataContainer`
|
||||
- `pcse.models`
|
||||
|
||||
اگر `pcse` نصب نباشد، `None` برمیگرداند.
|
||||
|
||||
### `_resolve_model_class(bindings, model_name)`
|
||||
کلاس مدل PCSE را با نامی مثل `Wofost81_NWLP_CWB_CNB` پیدا میکند.
|
||||
|
||||
---
|
||||
|
||||
## `PreparedSimulationInput`
|
||||
|
||||
این dataclass ورودیهای نرمالشده برای اجرای مدل را نگه میدارد:
|
||||
|
||||
- `weather`
|
||||
- `soil`
|
||||
- `crop`
|
||||
- `site`
|
||||
- `agromanagement`
|
||||
|
||||
این ساختار باعث میشود manager با یک payload استاندارد کار کند.
|
||||
|
||||
---
|
||||
|
||||
## بخش سوم: `PcseSimulationManager`
|
||||
|
||||
این کلاس فقط مسئول اجرای موتور شبیهسازی است و وارد منطق ذخیره سناریوها نمیشود.
|
||||
|
||||
### `__init__(model_name="Wofost81_NWLP_CWB_CNB")`
|
||||
مدل PCSE مورد استفاده را مشخص میکند.
|
||||
|
||||
مدل پیشفرض:
|
||||
|
||||
```python
|
||||
Wofost81_NWLP_CWB_CNB
|
||||
```
|
||||
|
||||
### `run_simulation(...)`
|
||||
ورودی خام میگیرد، normalize میکند، dependencyهای PCSE را load میکند، و شبیهسازی را اجرا میکند.
|
||||
|
||||
پارامترها:
|
||||
|
||||
- `weather`
|
||||
- `soil`
|
||||
- `crop_parameters`
|
||||
- `agromanagement`
|
||||
- `site_parameters`
|
||||
|
||||
خروجی:
|
||||
|
||||
```python
|
||||
{
|
||||
"engine": "pcse",
|
||||
"model_name": "Wofost81_NWLP_CWB_CNB",
|
||||
"metrics": {...},
|
||||
"daily_output": [...],
|
||||
"summary_output": [...],
|
||||
"terminal_output": [...]
|
||||
}
|
||||
```
|
||||
|
||||
### `_run_with_pcse(prepared, bindings)`
|
||||
اجرای واقعی مدل را انجام میدهد.
|
||||
|
||||
جریان داخلی:
|
||||
|
||||
1. ساخت weather provider سفارشی از روی dictها
|
||||
2. ساخت `ParameterProvider`
|
||||
3. ساخت instance مدل PCSE
|
||||
4. اجرای `run_till_terminate()` یا `run()`
|
||||
5. گرفتن خروجیها
|
||||
6. تبدیل خروجی به فرم نهایی
|
||||
|
||||
### `_build_result(...)`
|
||||
metricهای کلیدی را از خروجیهای terminal/summary/daily استخراج میکند:
|
||||
|
||||
- `yield_estimate`
|
||||
- `biomass`
|
||||
- `max_lai`
|
||||
|
||||
اولویت انتخاب metricها:
|
||||
|
||||
1. terminal
|
||||
2. summary
|
||||
3. آخرین رکورد daily
|
||||
|
||||
---
|
||||
|
||||
## بخش چهارم: `CropSimulationService`
|
||||
|
||||
این کلاس service layer سطح بالاتر است. علاوه بر اجرای مدل، سناریوها و runها را در دیتابیس ذخیره میکند.
|
||||
|
||||
مدلهای مرتبط:
|
||||
|
||||
- `SimulationScenario`
|
||||
- `SimulationRun`
|
||||
|
||||
### `__init__(manager=None)`
|
||||
اگر manager داده نشود، از `PcseSimulationManager()` پیشفرض استفاده میشود.
|
||||
|
||||
---
|
||||
|
||||
## متدهای public اصلی
|
||||
|
||||
### 1) `run_single_simulation(...)`
|
||||
برای اجرای یک سناریوی تکی.
|
||||
|
||||
پارامترها:
|
||||
|
||||
- `weather`
|
||||
- `soil`
|
||||
- `crop_parameters`
|
||||
- `agromanagement`
|
||||
- `site_parameters`
|
||||
- `irrigation_recommendation`
|
||||
- `fertilization_recommendation`
|
||||
- `name`
|
||||
|
||||
کارها:
|
||||
|
||||
1. merge کردن recommendationها داخل management
|
||||
2. ساخت `SimulationScenario` با نوع `SINGLE`
|
||||
3. ساخت `SimulationRun`
|
||||
4. اجرای سناریو
|
||||
|
||||
مهم:
|
||||
اگر recommendationهای آبیاری/کودهی بدهید، این متد آنها را به eventهای مدل تبدیل میکند.
|
||||
|
||||
نمونه:
|
||||
|
||||
```python
|
||||
from crop_simulation.services import run_single_simulation
|
||||
|
||||
result = run_single_simulation(
|
||||
weather=weather_payload,
|
||||
soil={"SMFCF": 0.34, "SMW": 0.12, "RDMSOL": 120.0},
|
||||
crop_parameters={"crop_name": "wheat", "TSUM1": 800},
|
||||
agromanagement=agromanagement_payload,
|
||||
site_parameters={"WAV": 40.0},
|
||||
irrigation_recommendation={
|
||||
"events": [
|
||||
{"date": "2026-04-25", "amount": 2.5, "efficiency": 0.8}
|
||||
]
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
### 2) `compare_crops(...)`
|
||||
برای مقایسه دو محصول.
|
||||
|
||||
ورودیهای اضافه:
|
||||
|
||||
- `crop_a`
|
||||
- `crop_b`
|
||||
|
||||
خروجی:
|
||||
|
||||
- سناریو با نوع `CROP_COMPARISON`
|
||||
- دو run
|
||||
- comparison شامل best run و yield gap
|
||||
|
||||
### 3) `recommend_best_crop(...)`
|
||||
برای مقایسه چند محصول و انتخاب بهترین گزینه.
|
||||
|
||||
ورودی مهم:
|
||||
|
||||
- `crops: list[dict]`
|
||||
|
||||
شرط:
|
||||
|
||||
- حداقل دو crop باید وجود داشته باشد
|
||||
|
||||
خروجی سادهشده:
|
||||
|
||||
```python
|
||||
{
|
||||
"scenario_id": ...,
|
||||
"scenario_type": "crop_comparison",
|
||||
"recommended_crop": {
|
||||
"run_key": "...",
|
||||
"label": "...",
|
||||
"expected_yield_estimate": ...
|
||||
},
|
||||
"candidates": [...],
|
||||
"raw_result": {...}
|
||||
}
|
||||
```
|
||||
|
||||
### 4) `compare_fertilization_strategies(...)`
|
||||
برای مقایسه چند strategy کودهی روی یک crop ثابت.
|
||||
|
||||
ورودی ویژه:
|
||||
|
||||
```python
|
||||
strategies = [
|
||||
{
|
||||
"label": "base",
|
||||
"agromanagement": [...]
|
||||
},
|
||||
{
|
||||
"label": "high_n",
|
||||
"agromanagement": [...]
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
این متد برای هر strategy یک run میسازد و بهترین استراتژی را بر اساس `yield_estimate` انتخاب میکند.
|
||||
|
||||
### 5) `get_scenario_result(scenario_id)`
|
||||
نتیجه ذخیرهشده یک سناریو را از دیتابیس برمیگرداند.
|
||||
|
||||
خروجی شامل:
|
||||
|
||||
- اطلاعات scenario
|
||||
- اطلاعات همه runها
|
||||
- status
|
||||
- input payload
|
||||
- result payload
|
||||
- error message
|
||||
|
||||
---
|
||||
|
||||
## متدهای داخلی مهم در `CropSimulationService`
|
||||
|
||||
### `_execute_scenario(...)`
|
||||
قلب اجرای سناریو است.
|
||||
|
||||
جریان:
|
||||
|
||||
1. status سناریو را `RUNNING` میکند
|
||||
2. تکتک runها را اجرا میکند
|
||||
3. خروجی هر run را ذخیره میکند
|
||||
4. اگر exception رخ دهد:
|
||||
- همان run را `FAILURE` میکند
|
||||
- سناریو را `FAILURE` میکند
|
||||
- خطا را ذخیره میکند
|
||||
5. اگر همه چیز موفق باشد:
|
||||
- `scenario_result` میسازد
|
||||
- سناریو را `SUCCESS` میکند
|
||||
|
||||
### `_build_scenario_result(scenario, results)`
|
||||
خروجی سطح سناریو را میسازد.
|
||||
|
||||
رفتار بر اساس نوع سناریو:
|
||||
|
||||
- `SINGLE`:
|
||||
- فقط `result` برمیگرداند
|
||||
- `CROP_COMPARISON`:
|
||||
- comparison میسازد
|
||||
- بهترین run را مشخص میکند
|
||||
- `yield_gap` میسازد
|
||||
- `FERTILIZATION_COMPARISON`:
|
||||
- recommendation برای بهترین strategy میسازد
|
||||
|
||||
---
|
||||
|
||||
## wrapperهای سطح ماژول
|
||||
|
||||
در انتهای فایل این wrapperها وجود دارند:
|
||||
|
||||
- `run_single_simulation(**kwargs)`
|
||||
- `compare_crops(**kwargs)`
|
||||
- `recommend_best_crop(**kwargs)`
|
||||
- `compare_fertilization_strategies(**kwargs)`
|
||||
|
||||
همه آنها با `@transaction.atomic` تزئین شدهاند.
|
||||
|
||||
یعنی اگر بخواهید ساده از بیرون صدا بزنید، لازم نیست خودتان instance بسازید:
|
||||
|
||||
```python
|
||||
from crop_simulation.services import recommend_best_crop
|
||||
|
||||
result = recommend_best_crop(
|
||||
weather=weather_payload,
|
||||
soil=soil_payload,
|
||||
crops=[crop_a, crop_b, crop_c],
|
||||
agromanagement=agromanagement_payload,
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## نحوه ارتباط با مدلهای دیتابیس
|
||||
|
||||
### `SimulationScenario`
|
||||
نماینده یک سناریوی کلی است.
|
||||
|
||||
مثالها:
|
||||
|
||||
- single run
|
||||
- crop comparison
|
||||
- fertilization comparison
|
||||
|
||||
### `SimulationRun`
|
||||
نماینده هر اجرای منفرد داخل یک سناریو است.
|
||||
|
||||
مثلا در `compare_crops`:
|
||||
|
||||
- یک `SimulationScenario`
|
||||
- دو `SimulationRun`
|
||||
|
||||
---
|
||||
|
||||
## ارتباط `crop_simulation/services.py` با `crop_simulation/apps.py`
|
||||
|
||||
فایل `crop_simulation/apps.py` این متد را expose میکند:
|
||||
|
||||
```python
|
||||
def get_recommendation_optimizer(self):
|
||||
return self.recommendation_optimizer
|
||||
```
|
||||
|
||||
این optimizer در فایل `crop_simulation/recommendation_optimizer.py` ساخته میشود و برای recommendationهای آبیاری و کودهی استفاده میشود.
|
||||
|
||||
نکته مهم:
|
||||
|
||||
- `services.py` موتور اجرای سناریوهاست
|
||||
- `recommendation_optimizer.py` روی همین موتور سناریوهای candidate میسازد
|
||||
- `apps.py` فقط نقطه دسترسی مرکزی به optimizer است
|
||||
|
||||
یعنی:
|
||||
|
||||
```python
|
||||
optimizer = apps.get_app_config("crop_simulation").get_recommendation_optimizer()
|
||||
```
|
||||
|
||||
و بعد optimizer در داخل خودش از `CropSimulationService` استفاده میکند.
|
||||
|
||||
---
|
||||
|
||||
## ارتباط با `irrigation/apps.py`
|
||||
|
||||
فایل `irrigation/apps.py` خودش شبیهسازی اجرا نمیکند؛ بلکه تنظیمات default برای optimizer آبیاری را نگه میدارد.
|
||||
|
||||
### فیلدهای مهم
|
||||
|
||||
#### `tone_file`
|
||||
مسیر tone مربوط به LLM:
|
||||
|
||||
```python
|
||||
config/tones/irrigation_tone.txt
|
||||
```
|
||||
|
||||
#### `optimizer_defaults`
|
||||
این property تنظیمات پایه بهینهسازی آبیاری را برمیگرداند:
|
||||
|
||||
- `validity_days`
|
||||
- `minimum_event_mm`
|
||||
- `significant_rain_threshold_mm`
|
||||
- `stage_targets`
|
||||
- `strategy_profiles`
|
||||
|
||||
### `stage_targets`
|
||||
هدف رطوبت یا رفتار پایه برای stageهای مختلف:
|
||||
|
||||
- `initial`
|
||||
- `vegetative`
|
||||
- `flowering`
|
||||
- `fruiting`
|
||||
|
||||
### `strategy_profiles`
|
||||
سه سناریوی پایه برای optimizer:
|
||||
|
||||
- `conservative`
|
||||
- `balanced`
|
||||
- `protective`
|
||||
|
||||
هر سناریو مشخص میکند:
|
||||
|
||||
- ضریب آب (`multiplier`)
|
||||
- ضریب تعداد دفعات (`frequency_factor`)
|
||||
- تعداد event پایه (`event_count`)
|
||||
|
||||
### نحوه استفاده در کد
|
||||
|
||||
در optimizer آبیاری معمولا به شکل زیر خوانده میشود:
|
||||
|
||||
```python
|
||||
defaults = apps.get_app_config("irrigation").get_optimizer_defaults()
|
||||
```
|
||||
|
||||
سپس این defaults به سناریوهای recommendation تبدیل میشوند و در صورت نیاز به `run_single_simulation()` پاس داده میشوند.
|
||||
|
||||
### نقش آن در ارتباط با `services.py`
|
||||
|
||||
ارتباط غیرمستقیم است:
|
||||
|
||||
1. `irrigation/apps.py` تنظیمات baseline را میدهد
|
||||
2. optimizer با این تنظیمات candidate strategy میسازد
|
||||
3. strategyها به recommendation event تبدیل میشوند
|
||||
4. `crop_simulation/services.py` آنها را داخل agromanagement merge و اجرا میکند
|
||||
|
||||
---
|
||||
|
||||
## ارتباط با `fertilization/apps.py`
|
||||
|
||||
این فایل مشابه irrigation است اما برای منطق کودهی.
|
||||
|
||||
### `tone_file`
|
||||
|
||||
```python
|
||||
config/tones/fertilization_tone.txt
|
||||
```
|
||||
|
||||
### `optimizer_defaults`
|
||||
این تنظیمات را میدهد:
|
||||
|
||||
- `validity_days`
|
||||
- `rain_delay_threshold_mm`
|
||||
- `stage_targets`
|
||||
- `strategy_profiles`
|
||||
|
||||
### `stage_targets`
|
||||
برای هر stage اطلاعات زیر مشخص میشود:
|
||||
|
||||
- `n`
|
||||
- `p`
|
||||
- `k`
|
||||
- `formula`
|
||||
- `application_method`
|
||||
- `timing`
|
||||
|
||||
### `strategy_profiles`
|
||||
سناریوهای پایه:
|
||||
|
||||
- `maintenance`
|
||||
- `balanced`
|
||||
- `corrective`
|
||||
|
||||
هرکدام مشخص میکنند:
|
||||
|
||||
- ضریب مصرف (`multiplier`)
|
||||
- focus تغذیهای
|
||||
- روش مصرف
|
||||
- override فرمول در صورت نیاز
|
||||
|
||||
### نحوه استفاده در کد
|
||||
|
||||
```python
|
||||
defaults = apps.get_app_config("fertilization").get_optimizer_defaults()
|
||||
```
|
||||
|
||||
سپس optimizer با این defaults چند strategy میسازد. اگر لازم باشد این strategyها به `compare_fertilization_strategies()` یا `run_single_simulation()` داده میشوند.
|
||||
|
||||
### ارتباط آن با `services.py`
|
||||
|
||||
ارتباط باز هم غیرمستقیم است:
|
||||
|
||||
1. `fertilization/apps.py` پروفایل stage و strategy را میدهد
|
||||
2. optimizer از روی آن strategy تولید میکند
|
||||
3. strategy به eventهای `apply_n` تبدیل میشود
|
||||
4. `services.py` این eventها را داخل agromanagement merge میکند
|
||||
5. سناریو اجرا و مقایسه میشود
|
||||
|
||||
---
|
||||
|
||||
## الگوی ارتباط کامل بین سه بخش
|
||||
|
||||
### سناریوی آبیاری
|
||||
|
||||
```text
|
||||
irrigation/apps.py
|
||||
-> optimizer_defaults
|
||||
-> recommendation optimizer
|
||||
-> irrigation recommendation events
|
||||
-> crop_simulation/services.py:_merge_management_recommendations()
|
||||
-> run_single_simulation()
|
||||
-> PCSE run
|
||||
-> scenario/run result
|
||||
```
|
||||
|
||||
### سناریوی کودهی
|
||||
|
||||
```text
|
||||
fertilization/apps.py
|
||||
-> optimizer_defaults
|
||||
-> recommendation optimizer
|
||||
-> fertilization recommendation events
|
||||
-> crop_simulation/services.py:_merge_management_recommendations()
|
||||
-> compare_fertilization_strategies() / run_single_simulation()
|
||||
-> PCSE run
|
||||
-> best strategy result
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## نمونه استفاده واقعی
|
||||
|
||||
### اجرای یک شبیهسازی ساده
|
||||
|
||||
```python
|
||||
from crop_simulation.services import run_single_simulation
|
||||
|
||||
result = run_single_simulation(
|
||||
weather=[
|
||||
{
|
||||
"DAY": "2026-04-01",
|
||||
"LAT": 35.7,
|
||||
"LON": 51.4,
|
||||
"ELEV": 1200,
|
||||
"IRRAD": 16000000,
|
||||
"TMIN": 11,
|
||||
"TMAX": 22,
|
||||
"VAP": 12,
|
||||
"WIND": 2.4,
|
||||
"RAIN": 0.8,
|
||||
"E0": 0.35,
|
||||
"ES0": 0.3,
|
||||
"ET0": 0.32,
|
||||
}
|
||||
],
|
||||
soil={"SMFCF": 0.34, "SMW": 0.12, "RDMSOL": 120.0},
|
||||
crop_parameters={"crop_name": "wheat", "TSUM1": 800, "YIELD_SCALE": 1.0},
|
||||
agromanagement=[
|
||||
{
|
||||
"2026-04-01": {
|
||||
"CropCalendar": {
|
||||
"crop_name": "wheat",
|
||||
"variety_name": "winter-wheat",
|
||||
"crop_start_date": "2026-04-05",
|
||||
"crop_start_type": "sowing",
|
||||
"crop_end_date": "2026-09-01",
|
||||
"crop_end_type": "harvest",
|
||||
"max_duration": 180,
|
||||
},
|
||||
"TimedEvents": [],
|
||||
"StateEvents": [],
|
||||
}
|
||||
}
|
||||
],
|
||||
site_parameters={"WAV": 40.0},
|
||||
)
|
||||
```
|
||||
|
||||
### مقایسه دو محصول
|
||||
|
||||
```python
|
||||
from crop_simulation.services import compare_crops
|
||||
|
||||
result = compare_crops(
|
||||
weather=weather_payload,
|
||||
soil=soil_payload,
|
||||
crop_a={"crop_name": "wheat", "TSUM1": 800},
|
||||
crop_b={"crop_name": "maize", "TSUM1": 900},
|
||||
agromanagement=agromanagement_payload,
|
||||
site_parameters={"WAV": 40.0},
|
||||
)
|
||||
```
|
||||
|
||||
### مقایسه strategyهای کودهی
|
||||
|
||||
```python
|
||||
from crop_simulation.services import compare_fertilization_strategies
|
||||
|
||||
result = compare_fertilization_strategies(
|
||||
weather=weather_payload,
|
||||
soil=soil_payload,
|
||||
crop_parameters={"crop_name": "wheat", "TSUM1": 800},
|
||||
strategies=[
|
||||
{"label": "base", "agromanagement": agm_base},
|
||||
{"label": "high_n", "agromanagement": agm_high_n},
|
||||
],
|
||||
site_parameters={"WAV": 40.0},
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## نکات مهم توسعه
|
||||
|
||||
### 1. نقطه اصلی inject کردن توصیهها
|
||||
اگر بخواهید recommendationهای جدید را وارد شبیهسازی کنید، مهمترین نقطه:
|
||||
|
||||
```python
|
||||
_merge_management_recommendations()
|
||||
```
|
||||
|
||||
### 2. نقطه اصلی اجرای موتور
|
||||
اگر بخواهید backend engine عوض شود یا مدل جدید اضافه شود:
|
||||
|
||||
```python
|
||||
PcseSimulationManager.run_simulation()
|
||||
```
|
||||
|
||||
### 3. نقطه اصلی مدیریت lifecycle سناریو
|
||||
اگر بخواهید queueing، logging یا audit بیشتری اضافه کنید:
|
||||
|
||||
```python
|
||||
CropSimulationService._execute_scenario()
|
||||
```
|
||||
|
||||
### 4. ارتباط با اپهای recommendation
|
||||
اگر stageها یا strategyهای آبیاری/کودهی تغییر کنند، باید این فایلها بررسی شوند:
|
||||
|
||||
- `irrigation/apps.py`
|
||||
- `fertilization/apps.py`
|
||||
|
||||
چون optimizer از آنها defaultها را میخواند.
|
||||
|
||||
---
|
||||
|
||||
## جمعبندی
|
||||
|
||||
اگر بخواهیم نقش هر فایل را در یک جمله بگوییم:
|
||||
|
||||
- `crop_simulation/services.py`: اجرای شبیهسازی، ساخت scenario/run، و merge کردن recommendationها با management
|
||||
- `crop_simulation/apps.py`: نقطه دسترسی مرکزی به optimizer
|
||||
- `irrigation/apps.py`: تنظیمات پایه برای سناریوهای بهینهسازی آبیاری
|
||||
- `fertilization/apps.py`: تنظیمات پایه برای سناریوهای بهینهسازی کودهی
|
||||
|
||||
و زنجیره کلی این است:
|
||||
|
||||
```text
|
||||
defaults in app config
|
||||
-> optimizer
|
||||
-> recommendation events
|
||||
-> crop_simulation/services.py
|
||||
-> PCSE execution
|
||||
-> scenario result
|
||||
```
|
||||
|
||||
اگر بخواهید، قدم بعدی میتوانم یک فایل دوم هم بسازم که فقط نمونه request/response واقعی برای هر تابع و هر سناریو را بهصورت cookbook نشان بدهد.
|
||||
Reference in New Issue
Block a user