DmitrMakeev commited on
Commit
ed10de7
·
verified ·
1 Parent(s): 8f645d5

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +144 -101
app.py CHANGED
@@ -790,74 +790,75 @@ NUTRIENT_CONTENT_IN_FERTILIZERS = {
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
- # Коэффициенты для расчета EC (мСм/см на ppm)
806
- self.ec_coefficients = {
807
- 'P': 0.0012, 'K': 0.0018, 'Mg': 0.0015,
808
- 'Ca': 0.0016, 'S': 0.0014,
809
- 'N (NO3-)': 0.0017, 'N (NH4+)': 0.0019
810
  }
811
 
812
- # Новая структура весов для компенсации
813
- self.compensation_weights = {
814
- 'POTASSIUM_NITRATE': {'weight': 0.5, 'fert': 'Калий азотнокислый', 'main_element': 'K'},
815
- 'CALCIUM_NITRATE': {'weight': 0.3, 'fert': 'Кальциевая селитра', 'main_element': 'Ca'},
816
- 'POTASSIUM_SULFATE': {'weight': 0.2, 'fert': 'Калий сернокислый', 'main_element': 'K'}
 
 
817
  }
818
 
819
- def _apply_fertilizer(self, fert_name: str, element: str, ppm: float):
820
- """Основной метод внесения удобрений с расчетом EC"""
821
- try:
822
- if ppm <= 0:
823
- return
 
 
824
 
825
- content = self.fertilizers[fert_name][element]
826
- grams = (ppm * self.volume) / (content * 1000)
827
- milligrams = grams * 1000
 
 
 
 
 
828
 
829
  if fert_name not in self.results:
830
- self.results[fert_name] = {
831
  'граммы': 0.0,
832
  'миллиграммы': 0,
833
- 'вклад в EC': 0.0,
834
- 'внесенные ppm': {}
835
  }
836
-
 
 
 
837
  self.results[fert_name]['граммы'] += grams
838
  self.results[fert_name]['миллиграммы'] = int(self.results[fert_name]['граммы'] * 1000)
839
 
840
- # Расчет EC и внесенных ppm
841
  fert_ec = 0.0
842
- for el, percent in self.fertilizers[fert_name].items():
843
  added_ppm = (grams * percent * 1000) / self.volume
844
- self.actual_profile[el] += added_ppm
845
- self.results[fert_name]['внесенные ppm'][el] = self.results[fert_name]['внесенные ppm'].get(el, 0) + added_ppm
846
- fert_ec += added_ppm * self.ec_coefficients.get(el, 0.0015)
847
-
848
  self.results[fert_name]['вклад в EC'] += fert_ec
849
  self.total_ec += fert_ec
850
-
851
  except KeyError as e:
852
- raise ValueError(f"Ошибка в данных удобрений: {str(e)}")
853
-
854
- def calculate_ec(self) -> float:
855
- """Возвращает общую электропроводность (EC)"""
856
- return round(self.total_ec, 2)
857
 
858
  def _compensate_element(self, element: str, needed_ppm: float):
859
- """Исправленная компенсация с учетом новых весов и приоритетов азота"""
860
- # Находим подходящие удобрения для элемента
861
  suitable_ferts = []
862
  for fert_key, fert_data in self.compensation_weights.items():
863
  fert_name = fert_data['fert']
@@ -869,78 +870,120 @@ class NutrientCalculator:
869
  })
870
 
871
  if not suitable_ferts:
872
- raise ValueError(f"Нет удобрений для элемента {element}")
873
 
874
- # Рассчитываем общий вес для нормализации
875
  total_weight = sum(f['weight'] for f in suitable_ferts)
876
-
877
- # Распределяем дефицит пропорционально весам
878
- remaining_ppm = needed_ppm
879
  for fert in suitable_ferts:
880
- if remaining_ppm <= 0:
881
- break
882
-
883
- # Вычисляем долю удобрения
884
  share = fert['weight'] / total_weight
885
- ppm_to_apply = remaining_ppm * share
886
-
887
- # Вносим удобрение
888
- self._apply_fertilizer(fert['name'], element, ppm_to_apply)
889
-
890
- # Обновляем остаток дефицита
891
- remaining_ppm = self.target_profile[element] - self.actual_profile[element]
 
892
 
893
  def calculate(self):
894
- """Основной расчет с учетом новых приоритетов"""
895
  try:
896
- # 1. Вносим магний (сульфат магния)
897
- self._apply_fertilizer("Сульфат магния", "Mg", self.target_profile['Mg'])
 
 
 
898
 
899
- # 2. Вносим аммонийный азот
900
- self._apply_fertilizer("Аммоний азотнокислый", "N (NH4+)", self.target_profile['N (NH4+)'])
901
 
902
- # 3. Компенсируем нитратный азот с учетом новых весов
903
- if self.target_profile['N (NO3-)'] > 0:
904
- self._compensate_element("N (NO3-)", self.target_profile['N (NO3-)'])
905
 
906
- # 4. Вносим фосфор
907
- self._apply_fertilizer("Монофосфат калия", "P", self.target_profile['P'])
 
 
 
908
 
909
- # 5. Компенсируем калий с учетом новых весов
910
- if self.target_profile['K'] > self.actual_profile['K']:
911
- self._compensate_element("K", self.target_profile['K'] - self.actual_profile['K'])
912
 
913
- # 6. Компенсируем кальций (если нужно)
914
- ca_needed = self.target_profile['Ca'] - self.actual_profile['Ca']
915
- if ca_needed > 0:
916
- self._compensate_element("Ca", ca_needed)
917
 
918
- return self._prepare_results()
 
 
 
919
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
920
  except Exception as e:
921
- raise RuntimeError(f"Ошибка расчета: {str(e)}")
922
-
923
- def _prepare_results(self) -> dict:
924
- """Подготовка результатов для API"""
925
- return {
926
- 'actual_profile': {k: round(v, 2) for k, v in self.actual_profile.items()},
927
- 'fertilizers': {
928
- fert: {
929
- 'граммы': round(data['граммы'], 3),
930
- 'миллиграммы': data['миллиграммы'],
931
- 'вклад в EC': round(data['вклад в EC'], 3),
932
- 'внесенные ppm': {k: round(v, 2) for k, v in data['внесенные ppm'].items()}
933
- }
934
- for fert, data in self.results.items()
935
- },
936
- 'total_ec': self.calculate_ec(),
937
- 'total_ppm': round(sum(self.actual_profile.values()), 2),
938
- 'nitrogen_ratios': {
939
- 'NO3': round(self.target_profile['N (NO3-)'], 2),
940
- 'NH4': round(self.target_profile['N (NH4+)'], 2)
941
- },
942
- 'compensation_weights': self.compensation_weights
943
- }
944
 
945
 
946
  @app.route('/calculation', methods=['POST'])
 
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
+ self.compensation_weights = {
803
+ "POTASSIUM_NITRATE": {"weight": 0.5, "fert": "Калий азотнокислый", "main_element": "K"},
804
+ "CALCIUM_NITRATE": {"weight": 0.3, "fert": "Кальциевая селитра", "main_element": "Ca"},
805
+ "POTASSIUM_SULFATE": {"weight": 0.2, "fert": "Калий сернокислый", "main_element": "K"}
806
  }
807
 
808
+ # Расчёт азота (оставляем без изменений)
809
+ total_parts = NO3_RATIO + NH4_RATIO
810
+ self.target_profile['N (NO3-)'] = TOTAL_NITROGEN * (NO3_RATIO / total_parts)
811
+ self.target_profile['N (NH4+)'] = TOTAL_NITROGEN * (NH4_RATIO / total_parts)
812
+ self.initial_n_profile = {
813
+ "NO3-": self.target_profile['N (NO3-)'],
814
+ "NH4+": self.target_profile['N (NH4+)']
815
  }
816
 
817
+ def _label(self, element):
818
+ """Форматирование названий элементов для вывода"""
819
+ labels = {
820
+ 'N (NO3-)': 'NO3',
821
+ 'N (NH4+)': 'NH4'
822
+ }
823
+ return labels.get(element, element)
824
 
825
+ def _apply(self, fert_name, main_element, required_ppm):
826
+ """Внесение удобрения"""
827
+ if required_ppm <= 0:
828
+ return
829
+
830
+ try:
831
+ content = self.fertilizers[fert_name][main_element]
832
+ grams = (required_ppm * self.volume) / (content * 1000)
833
 
834
  if fert_name not in self.results:
835
+ result = {
836
  'граммы': 0.0,
837
  'миллиграммы': 0,
838
+ 'вклад в EC': 0.0
 
839
  }
840
+ for element in self.fertilizers[fert_name]:
841
+ result[f'внесет {self._label(element)}'] = 0.0
842
+ self.results[fert_name] = result
843
+
844
  self.results[fert_name]['граммы'] += grams
845
  self.results[fert_name]['миллиграммы'] = int(self.results[fert_name]['граммы'] * 1000)
846
 
 
847
  fert_ec = 0.0
848
+ for element, percent in self.fertilizers[fert_name].items():
849
  added_ppm = (grams * percent * 1000) / self.volume
850
+ self.results[fert_name][f'внесет {self._label(element)}'] += added_ppm
851
+ self.actual_profile[element] += added_ppm
852
+ fert_ec += added_ppm * EC_COEFFICIENTS.get(element, 0.0015)
853
+
854
  self.results[fert_name]['вклад в EC'] += fert_ec
855
  self.total_ec += fert_ec
 
856
  except KeyError as e:
857
+ print(f"Ошибка: отсутствует элемент {str(e)} в удобрении {fert_name}")
858
+ raise
 
 
 
859
 
860
  def _compensate_element(self, element: str, needed_ppm: float):
861
+ """Компенсация дефицита элемента с учетом весов"""
 
862
  suitable_ferts = []
863
  for fert_key, fert_data in self.compensation_weights.items():
864
  fert_name = fert_data['fert']
 
870
  })
871
 
872
  if not suitable_ferts:
873
+ raise ValueError(f"Нет подходящих удобрений для элемента {element}")
874
 
 
875
  total_weight = sum(f['weight'] for f in suitable_ferts)
 
 
 
876
  for fert in suitable_ferts:
 
 
 
 
877
  share = fert['weight'] / total_weight
878
+ ppm_to_apply = needed_ppm * share
879
+ self._apply(fert['name'], element, ppm_to_apply)
880
+
881
+ def _apply_k_sulfate(self):
882
+ """Внесение калия сернокислого с учетом весов"""
883
+ s_def = self.target_profile['S'] - self.actual_profile['S']
884
+ if s_def > 0.1:
885
+ self._compensate_element("S", s_def)
886
 
887
  def calculate(self):
888
+ """Основной расчет"""
889
  try:
890
+ # 1. Вносим магний
891
+ self._apply("Сульфат магния", "Mg", self.target_profile['Mg'])
892
+
893
+ # 2. Вносим кальций
894
+ self._apply("Кальциевая селитра", "Ca", self.target_profile['Ca'])
895
 
896
+ # 3. Вносим фосфор
897
+ self._apply("Монофосфат калия", "P", self.target_profile['P'])
898
 
899
+ # 4. Вносим аммонийный азот (логика не меняется)
900
+ self._apply("Аммоний азотнокислый", "N (NH4+)", self.target_profile['N (NH4+)'])
 
901
 
902
+ # 5. Компенсируем нитратный азот (логика не меняется)
903
+ current_no3 = self.actual_profile['N (NO3-)']
904
+ no3_needed = self.target_profile['N (NO3-)'] - current_no3
905
+ if no3_needed > 0.1:
906
+ self._apply("Калий азотнокислый", "N (NO3-)", no3_needed)
907
 
908
+ # 6. Вносим серу и калий с учетом весов
909
+ self._apply_k_sulfate()
 
910
 
911
+ # 7. Компенсируем остаточный дефицит калия с учетом весов
912
+ k_deficit = self.target_profile['K'] - self.actual_profile['K']
913
+ if k_deficit > 0.1:
914
+ self._compensate_element("K", k_deficit)
915
 
916
+ return self.results
917
+ except Exception as e:
918
+ print(f"Ошибка при расчёте: {str(e)}")
919
+ raise
920
 
921
+ def calculate_ec(self):
922
+ """Расчет общей электропроводности"""
923
+ return round(self.total_ec, 2)
924
+
925
+ def print_report(self):
926
+ """Вывод отчета"""
927
+ try:
928
+ print("\n" + "="*60)
929
+ print("ПРОФИЛЬ ПИТАТЕЛЬНОГО РАСТВОРА (ИТОГО):")
930
+ print("="*60)
931
+ table = [[el, round(self.actual_profile[el], 1)] for el in self.actual_profile]
932
+ print(tabulate(table, headers=["Элемент", "ppm"]))
933
+
934
+ print("\nИсходный расчёт азота:")
935
+ for form, val in self.initial_n_profile.items():
936
+ print(f" {form}: {round(val, 1)} ppm")
937
+
938
+ print("\n" + "="*60)
939
+ print(f"РАСЧЕТ ДЛЯ {self.volume} ЛИТРОВ РАСТВОРА")
940
+ print("="*60)
941
+ print(f"Общая концентрация: {round(sum(self.actual_profile.values()), 1)} ppm")
942
+ print(f"EC: {self.calculate_ec()} mS/cm")
943
+
944
+ print("\nРЕКОМЕНДУЕМЫЕ УДОБРЕНИЯ:")
945
+ fert_table = []
946
+ for fert, data in self.results.items():
947
+ adds = [f"+{k}: {v:.1f} ppm" for k, v in data.items() if k.startswith('внесет')]
948
+ fert_table.append([
949
+ fert,
950
+ round(data['граммы'], 3),
951
+ data['миллиграммы'],
952
+ round(data['вклад в EC'], 3),
953
+ "\n".join(adds)
954
+ ])
955
+ print(tabulate(fert_table,
956
+ headers=["Удобрение", "Граммы", "Миллиграммы", "EC (мСм/см)", "Добавит"]))
957
+
958
+ print("\nОСТАТОЧНЫЙ ДЕФИЦИТ:")
959
+ deficit = {
960
+ k: round(self.target_profile[k] - self.actual_profile[k], 1)
961
+ for k in self.target_profile
962
+ if abs(self.target_profile[k] - self.actual_profile[k]) > 0.1
963
+ }
964
+ if deficit:
965
+ for el, val in deficit.items():
966
+ print(f" {el}: {val} ppm")
967
+ else:
968
+ print(" Все элементы покрыты полностью")
969
  except Exception as e:
970
+ print(f"Ошибка при выводе отчёта: {str(e)}")
971
+ raise
972
+
973
+ if __name__ == "__main__":
974
+ # Убедитесь, что эти константы определены
975
+ TOTAL_NITROGEN = 155.0 # Примерное значение, замените на своё
976
+ NO3_RATIO = 9.0 # Примерное значение, замените на своё
977
+ NH4_RATIO = 1.0 # Примерное значение, замените на своё
978
+ VOLUME_LITERS = 1.0 # Примерное значение, замените на своё
979
+
980
+ try:
981
+ calculator = NutrientCalculator(volume_liters=VOLUME_LITERS)
982
+ calculator.calculate()
983
+ calculator.print_report()
984
+ except Exception as e:
985
+ print(f"Критическая ошибка: {str(e)}")
986
+
 
 
 
 
 
 
987
 
988
 
989
  @app.route('/calculation', methods=['POST'])