DmitrMakeev commited on
Commit
8470169
·
verified ·
1 Parent(s): 856c5f2

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +108 -139
app.py CHANGED
@@ -790,165 +790,134 @@ NUTRIENT_CONTENT_IN_FERTILIZERS = {
790
  }
791
 
792
  class NutrientCalculator:
793
- def __init__(self, volume_liters=1.0):
794
  self.volume = volume_liters
795
  self.results = {}
796
- self.target_profile = BASE_PROFILE.copy()
797
- self.actual_profile = {k: 0.0 for k in BASE_PROFILE}
798
- self.fertilizers = NUTRIENT_CONTENT_IN_FERTILIZERS
799
- self.total_ec = 0.0
800
-
801
- # Расчёт азота
802
- total_parts = NO3_RATIO + NH4_RATIO
803
- self.target_profile['N (NO3-)'] = TOTAL_NITROGEN * (NO3_RATIO / total_parts)
804
- self.target_profile['N (NH4+)'] = TOTAL_NITROGEN * (NH4_RATIO / total_parts)
805
- self.initial_n_profile = {
806
- "NO3-": self.target_profile['N (NO3-)'],
807
- "NH4+": self.target_profile['N (NH4+)']
808
  }
809
-
810
- def _label(self, element):
811
- """Форматирование названий элементов для вывода"""
812
- labels = {
813
- 'N (NO3-)': 'NO3',
814
- 'N (NH4+)': 'NH4'
 
 
 
815
  }
816
- return labels.get(element, element)
817
 
818
- def calculate(self):
 
819
  try:
820
- self._apply("Сульфат магния", "Mg", self.target_profile['Mg'])
821
- self._apply("Кальциевая селитра", "Ca", self.target_profile['Ca'])
822
- self._apply("Монофосфат калия", "P", self.target_profile['P'])
823
- self._apply("Аммоний азотнокислый", "N (NH4+)", self.target_profile['N (NH4+)'])
824
-
825
- current_no3 = self.actual_profile['N (NO3-)']
826
- no3_needed = self.target_profile['N (NO3-)'] - current_no3
827
-
828
- if no3_needed > 0.1:
829
- self._apply("Калий азотнокислый", "N (NO3-)", no3_needed)
830
-
831
- self._apply_k_sulfate()
832
-
833
- k_deficit = self.target_profile['K'] - self.actual_profile['K']
834
- if k_deficit > 0.1:
835
- self._apply("Калий азотнокислый", "K", k_deficit)
836
-
837
- return self.results
838
- except Exception as e:
839
- print(f"Ошибка при расчёте: {str(e)}")
840
- raise
841
 
842
- def _apply(self, fert_name, main_element, required_ppm):
843
- if required_ppm <= 0:
844
- return
845
-
846
- try:
847
- content = self.fertilizers[fert_name][main_element]
848
- grams = (required_ppm * self.volume) / (content * 1000)
849
 
850
  if fert_name not in self.results:
851
- result = {
852
- 'граммы': 0.0,
853
- 'миллиграммы': 0,
854
- 'вклад в EC': 0.0
855
  }
856
- for element in self.fertilizers[fert_name]:
857
- result[f'внесет {self._label(element)}'] = 0.0
858
- self.results[fert_name] = result
859
-
860
- self.results[fert_name]['граммы'] += grams
861
- self.results[fert_name]['миллиграммы'] += int(grams * 1000)
862
 
863
- fert_ec = 0.0
864
- for element, percent in self.fertilizers[fert_name].items():
865
  added_ppm = (grams * percent * 1000) / self.volume
866
- self.results[fert_name][f'внесет {self._label(element)}'] += added_ppm
867
- self.actual_profile[element] += added_ppm
868
- fert_ec += added_ppm * EC_COEFFICIENTS.get(element, 0.0015)
869
-
870
- self.results[fert_name]['вклад в EC'] += fert_ec
871
- self.total_ec += fert_ec
872
  except KeyError as e:
873
- print(f"Ошибка: отсутствует элемент {str(e)} в удобрении {fert_name}")
874
- raise
875
 
876
- def _apply_k_sulfate(self):
877
- fert = "Калий сернокислый"
878
- k_def = self.target_profile['K'] - self.actual_profile['K']
879
- s_def = self.target_profile['S'] - self.actual_profile['S']
880
 
881
- if k_def <= 0 and s_def <= 0:
882
- return
883
-
884
- try:
885
- if s_def > 0.1:
886
- s_content = self.fertilizers[fert]["S"]
887
- grams_s = (s_def * self.volume) / (s_content * 1000)
 
 
888
 
889
- k_content = self.fertilizers[fert]["K"]
890
- k_from_s = (grams_s * k_content * 1000) / self.volume
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
891
 
892
- if k_from_s > k_def and k_def > 0.1:
893
- grams = (k_def * self.volume) / (k_content * 1000)
894
- else:
895
- grams = grams_s
896
-
897
- self._apply(fert, "S", s_def)
898
- except Exception as e:
899
- print(f"Ошибка при расчёте сульфата калия: {str(e)}")
900
- raise
901
-
902
- def calculate_ec(self):
903
- return round(self.total_ec, 2)
904
 
905
- def print_report(self):
 
906
  try:
907
- print("\n" + "="*60)
908
- print("ПРОФИЛЬ ПИТАТЕЛЬНОГО РАСТВОРА (ИТОГО):")
909
- print("="*60)
910
- table = [[el, round(self.actual_profile[el], 1)] for el in self.actual_profile]
911
- print(tabulate(table, headers=["Элемент", "ppm"]))
912
-
913
- print("\nИсходный расчёт азота:")
914
- for form, val in self.initial_n_profile.items():
915
- print(f" {form}: {round(val, 1)} ppm")
916
-
917
- print("\n" + "="*60)
918
- print(f"РАСЧЕТ ДЛЯ {self.volume} ЛИТРОВ РАСТВОРА")
919
- print("="*60)
920
- print(f"Общая концентрация: {round(sum(self.actual_profile.values()), 1)} ppm")
921
- print(f"EC: {self.calculate_ec()} mS/cm")
922
-
923
- print("\nРЕКОМЕНДУЕМЫЕ УДОБРЕНИЯ:")
924
- fert_table = []
925
- for fert, data in self.results.items():
926
- adds = [f"+{k}: {v:.1f} ppm" for k, v in data.items() if k.startswith('внесет')]
927
- fert_table.append([
928
- fert,
929
- round(data['граммы'], 3),
930
- data['миллиграммы'],
931
- round(data['вклад в EC'], 3),
932
- "\n".join(adds)
933
- ])
934
- print(tabulate(fert_table,
935
- headers=["Удобрение", "Граммы", "Миллиграммы", "EC (мСм/см)", "Добавит"]))
936
-
937
- print("\nОСТАТОЧНЫЙ ДЕФИЦИТ:")
938
- deficit = {
939
- k: round(self.target_profile[k] - self.actual_profile[k], 1)
940
- for k in self.target_profile
941
- if abs(self.target_profile[k] - self.actual_profile[k]) > 0.1
942
- }
943
- if deficit:
944
- for el, val in deficit.items():
945
- print(f" {el}: {val} ppm")
946
- else:
947
- print(" Все элементы покрыты полностью")
948
  except Exception as e:
949
- print(f"Ошибка при выводе отчёта: {str(e)}")
950
- raise
951
-
 
 
 
 
 
 
 
 
 
 
 
 
 
952
  if __name__ == "__main__":
953
  try:
954
  calculator = NutrientCalculator(volume_liters=VOLUME_LITERS)
 
790
  }
791
 
792
  class NutrientCalculator:
793
+ def __init__(self, volume_liters: float = 1.0):
794
  self.volume = volume_liters
795
  self.results = {}
796
+ self.target_profile = {
797
+ 'P': 31.0, 'K': 210.0, 'Mg': 24.0,
798
+ 'Ca': 84.0, 'S': 56.439,
799
+ 'N (NO3-)': 0.0, 'N (NH4+)': 0.0
 
 
 
 
 
 
 
 
800
  }
801
+ self.actual_profile = {k: 0.0 for k in self.target_profile}
802
+ self.total_ec = 0.0
803
+ self.fertilizers = {}
804
+
805
+ # Веса для компенсации (приоритеты)
806
+ self.compensation_weights = {
807
+ 'POTASSIUM_NITRATE': {'weight': 0.5, 'fert': 'Калий азотнокислый', 'main_element': 'K'},
808
+ 'CALCIUM_NITRATE': {'weight': 0.3, 'fert': 'Кальциевая селитра', 'main_element': 'Ca'},
809
+ 'POTASSIUM_SULFATE': {'weight': 0.2, 'fert': 'Калий сернокислый', 'main_element': 'K'}
810
  }
 
811
 
812
+ def _apply_fertilizer(self, fert_name: str, element: str, ppm: float):
813
+ """Основной метод внесения удобрений"""
814
  try:
815
+ if ppm <= 0:
816
+ return
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
817
 
818
+ content = self.fertilizers[fert_name][element]
819
+ grams = (ppm * self.volume) / (content * 1000)
 
 
 
 
 
820
 
821
  if fert_name not in self.results:
822
+ self.results[fert_name] = {
823
+ 'grams': 0.0,
824
+ 'ppm_added': {}
 
825
  }
826
+
827
+ self.results[fert_name]['grams'] += grams
 
 
 
 
828
 
829
+ # Обновляем все элементы из удобрения
830
+ for el, percent in self.fertilizers[fert_name].items():
831
  added_ppm = (grams * percent * 1000) / self.volume
832
+ self.actual_profile[el] = self.actual_profile.get(el, 0) + added_ppm
833
+ self.results[fert_name]['ppm_added'][el] = self.results[fert_name]['ppm_added'].get(el, 0) + added_ppm
834
+
 
 
 
835
  except KeyError as e:
836
+ raise ValueError(f"Неверный элемент или удобрение: {str(e)}")
 
837
 
838
+ def _compensate_with_weights(self, element: str, needed_ppm: float):
839
+ """Компенсация с учётом весов"""
840
+ candidates = []
 
841
 
842
+ # Ищем все удобрения, которые содержат нужный элемент
843
+ for fert_name, fert_data in self.fertilizers.items():
844
+ if element in fert_data:
845
+ # Находим соответствующий вес в compensation_weights
846
+ weight = 0.0
847
+ for comp_key, comp_data in self.compensation_weights.items():
848
+ if comp_data['fert'] == fert_name and comp_data['main_element'] == element:
849
+ weight = comp_data['weight']
850
+ break
851
 
852
+ candidates.append({
853
+ 'fert_name': fert_name,
854
+ 'element': element,
855
+ 'weight': weight,
856
+ 'content': fert_data[element]
857
+ })
858
+
859
+ if not candidates:
860
+ raise ValueError(f"No fertilizers found to compensate {element}")
861
+
862
+ # Сортируем по весу (больший вес = выше приоритет)
863
+ candidates.sort(key=lambda x: x['weight'], reverse=True)
864
+
865
+ # Применяем удобрения по порядку, пока не компенсируем дефицит
866
+ remaining_ppm = needed_ppm
867
+ for candidate in candidates:
868
+ if remaining_ppm <= 0:
869
+ break
870
 
871
+ # Рассчитываем сколько можем внести этим удобрением
872
+ max_possible = remaining_ppm / candidate['weight'] if candidate['weight'] > 0 else remaining_ppm
873
+ to_apply = min(max_possible, remaining_ppm)
874
+
875
+ self._apply_fertilizer(candidate['fert_name'], candidate['element'], to_apply)
876
+ remaining_ppm -= to_apply
 
 
 
 
 
 
877
 
878
+ def calculate(self):
879
+ """Основная логика расчёта с компенсационными весами"""
880
  try:
881
+ # 1. Вносим магний и серу (без компенсации)
882
+ self._apply_fertilizer("Сульфат магния", "Mg", self.target_profile['Mg'])
883
+
884
+ # 2. Вносим аммонийный азот (без компенсации)
885
+ nh4_needed = self.target_profile['N (NH4+)'] - self.actual_profile['N (NH4+)']
886
+ if nh4_needed > 0:
887
+ self._apply_fertilizer("Аммоний азотнокислый", "N (NH4+)", nh4_needed)
888
+
889
+ # 3. Вносим нитратный азот с компенсацией
890
+ no3_needed = self.target_profile['N (NO3-)'] - self.actual_profile['N (NO3-)']
891
+ if no3_needed > 0:
892
+ self._compensate_with_weights("N (NO3-)", no3_needed)
893
+
894
+ # 4. Вносим фосфор (без компенсации)
895
+ self._apply_fertilizer("Монофосфат калия", "P", self.target_profile['P'])
896
+
897
+ # 5. Корректируем калий с компенсацией
898
+ k_needed = self.target_profile['K'] - self.actual_profile['K']
899
+ if k_needed > 0:
900
+ self._compensate_with_weights("K", k_needed)
901
+
902
+ return self._prepare_results()
903
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
904
  except Exception as e:
905
+ raise RuntimeError(f"Ошибка расчёта: {str(e)}")
906
+
907
+ def _prepare_results(self):
908
+ """Форматируем результаты для ответа"""
909
+ return {
910
+ 'profile': self.actual_profile,
911
+ 'fertilizers': self.results,
912
+ 'stats': {
913
+ 'total_ppm': sum(self.actual_profile.values()),
914
+ 'nitrogen_ratio': {
915
+ 'NO3': self.target_profile['N (NO3-)'],
916
+ 'NH4': self.target_profile['N (NH4+)']
917
+ },
918
+ 'compensation_weights_used': self.compensation_weights
919
+ }
920
+ }
921
  if __name__ == "__main__":
922
  try:
923
  calculator = NutrientCalculator(volume_liters=VOLUME_LITERS)