DmitrMakeev commited on
Commit
be7ca79
·
verified ·
1 Parent(s): 94f8e97

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +50 -134
app.py CHANGED
@@ -733,24 +733,23 @@ class NutrientCalculator:
733
  def __init__(self, volume_liters=1.0):
734
  self.volume = volume_liters
735
  self.target_profile = BASE_PROFILE.copy()
736
- self.fertilizers = NUTRIENT_CONTENT_IN_FERTILIZERS
 
 
 
737
  self.total_ec = 0.0
738
  self.best_solution = None
739
  self.min_difference = float('inf')
740
- self.max_recursion_depth = 1000 # Ограничение глубины рекурсии
741
  self.current_depth = 0
742
- self.fertilizer_list = list(self.fertilizers.keys())
743
 
744
  # Расчёт азота
745
  total_parts = NO3_RATIO + NH4_RATIO
746
  self.target_profile['N (NO3-)'] = TOTAL_NITROGEN * (NO3_RATIO / total_parts)
747
  self.target_profile['N (NH4+)'] = TOTAL_NITROGEN * (NH4_RATIO / total_parts)
748
 
749
- # Сохраняем исходный профиль азота
750
- self.initial_n_profile = {
751
- "NO3-": self.target_profile['N (NO3-)'],
752
- "NH4+": self.target_profile['N (NH4+)']
753
- }
754
 
755
  def calculate(self):
756
  try:
@@ -762,7 +761,8 @@ class NutrientCalculator:
762
  print("Оптимальная комбинация найдена!")
763
  return self.best_solution
764
  else:
765
- return self.best_solution or {"error": "Не удалось найти идеальную комбинацию"}
 
766
 
767
  except Exception as e:
768
  print(f"Ошибка при расчёте: {str(e)}")
@@ -773,8 +773,10 @@ class NutrientCalculator:
773
  if self.current_depth > self.max_recursion_depth:
774
  return False
775
 
776
- # Проверяем текущее решение
777
- current_diff = self._calculate_difference()
 
 
778
  if current_diff < self.min_difference:
779
  self.min_difference = current_diff
780
  self.best_solution = {
@@ -784,15 +786,16 @@ class NutrientCalculator:
784
  "difference": current_diff
785
  }
786
 
787
- if current_diff < 0.1: # Допустимая погрешность
788
  return True
789
 
790
  # Пробуем добавлять удобрения с текущего индекса
791
- for i in range(fertilizer_index, len(self.fertilizer_list)):
792
- fert_name = self.fertilizer_list[i]
793
-
 
794
  # Проверяем, можно ли применить удобрение
795
- if not self._can_apply_fertilizer(fert_name):
796
  continue
797
 
798
  # Пробуем добавить удобрение с текущим шагом
@@ -806,158 +809,71 @@ class NutrientCalculator:
806
  self._remove_fertilizer(fert_name, step)
807
 
808
  # Пробуем уменьшить шаг для более точного поиска
809
- if step > 0.1 and current_diff > 1.0:
810
  if self._backtrack_search(i, step / 2):
811
  return True
812
 
813
  return False
814
 
815
- def _can_apply_fertilizer(self, fert_name):
816
  """Проверяет, можно ли применить удобрение без перебора"""
817
- for element, content in self.fertilizers[fert_name].items():
818
  added_ppm = (1 * content * 1000) / self.volume
819
- if self.actual_profile[element] + added_ppm > self.target_profile[element] + 0.1:
820
  return False
821
  return True
822
 
823
  def _apply_fertilizer(self, fert_name, amount):
824
  """Добавляет указанное количество удобрени��"""
 
 
 
825
  if fert_name not in self.results:
826
- self._init_fertilizer_record(fert_name)
827
-
 
 
 
 
828
  self.results[fert_name]['граммы'] += amount
829
  self.results[fert_name]['миллиграммы'] += int(amount * 1000)
830
-
831
- added_ec = 0.0
832
- for element, content in self.fertilizers[fert_name].items():
833
- added_ppm = (amount * content * 1000) / self.volume
834
- self.results[fert_name][f'внесет {element}'] += added_ppm
835
- self.actual_profile[element] += added_ppm
836
- added_ec += added_ppm * EC_COEFFICIENTS.get(element, 0.0015)
837
-
838
- self.results[fert_name]['вклад в EC'] += added_ec
839
- self.total_ec += added_ec
840
 
841
  def _remove_fertilizer(self, fert_name, amount):
842
  """Удаляет указанное количество удобрения"""
 
 
 
843
  if fert_name in self.results:
844
  self.results[fert_name]['граммы'] -= amount
845
  self.results[fert_name]['миллиграммы'] -= int(amount * 1000)
846
-
847
- removed_ec = 0.0
848
- for element, content in self.fertilizers[fert_name].items():
849
- removed_ppm = (amount * content * 1000) / self.volume
850
- self.results[fert_name][f'внесет {element}'] -= removed_ppm
851
- self.actual_profile[element] -= removed_ppm
852
- removed_ec += removed_ppm * EC_COEFFICIENTS.get(element, 0.0015)
853
-
854
- self.results[fert_name]['вклад в EC'] -= removed_ec
855
- self.total_ec -= removed_ec
856
-
857
  if self.results[fert_name]['граммы'] <= 0.001:
858
  del self.results[fert_name]
859
 
860
- def _calculate_difference(self):
861
  """Вычисляет общее отклонение от целевого профиля"""
862
- return sum(
863
- abs(self.actual_profile[e] - self.target_profile[e])
864
- for e in self.target_profile
865
- )
866
-
867
- def _copy_results(self):
868
- """Создаёт глубокую копию результатов"""
869
- return {
870
- fert: data.copy()
871
- for fert, data in self.results.items()
872
- }
873
-
874
- def _init_fertilizer_record(self, fert_name):
875
- """Инициализирует запись для нового удобрения"""
876
- self.results[fert_name] = {
877
- 'граммы': 0.0,
878
- 'миллиграммы': 0,
879
- 'вклад в EC': 0.0
880
- }
881
- for elem in self.fertilizers[fert_name]:
882
- self.results[fert_name][f'внесет {elem}'] = 0.0
883
 
884
  def generate_report(self):
885
  """Генерация отчета о питательном растворе"""
886
  try:
887
- report = "============================================================\n"
888
- report += "ПРОФИЛЬ ПИТАТЕЛЬНОГО РАСТВОРА (ИТОГО):\n"
889
- report += "============================================================\n"
890
- report += f"{'Элемент':<10} {'ppm':>10}\n"
891
- report += "-" * 20 + "\n"
892
- for element, value in self.actual_profile.items():
893
- report += f"{element:<10} {value:>10.2f}\n"
894
-
895
- report += "\nИсходный расчёт азота:\n"
896
- report += f" NO3-: {self.initial_n_profile['NO3-']:.2f} ppm\n"
897
- report += f" NH4+: {self.initial_n_profile['NH4+']:.2f} ppm\n"
898
-
899
  return report
900
  except Exception as e:
901
  print(f"Ошибка при выводе отчёта: {str(e)}")
902
  raise
903
 
904
- def calculate_ec(self):
905
- return round(self.total_ec, 2)
906
-
907
- def print_initial_nitrogen_report(self):
908
- try:
909
- print("Исходный расчёт азота:")
910
- print(f" NO3-: {self.initial_n_profile['NO3-']} ppm")
911
- print(f" NH4+: {self.initial_n_profile['NH4+']} ppm")
912
- except Exception as e:
913
- print(f"Ошибка при выводе отчёта: {str(e)}")
914
- raise
915
- def print_report(self):
916
- try:
917
- print("\n" + "="*60)
918
- print("ПРОФИЛЬ ПИТАТЕЛЬНОГО РАСТВОРА (ИТОГО):")
919
- print("="*60)
920
- table = [[el, round(self.actual_profile[el], 1)] for el in self.actual_profile]
921
- print(tabulate(table, headers=["Элемент", "ppm"]))
922
-
923
- print("\nИсходный расчёт азота:")
924
- for form, val in self.initial_n_profile.items():
925
- print(f" {form}: {round(val, 1)} ppm")
926
-
927
- print("\n" + "="*60)
928
- print(f"РАСЧЕТ ДЛЯ {self.volume} ЛИТРОВ РАСТВОРА")
929
- print("="*60)
930
- print(f"Общая концентрация: {round(sum(self.actual_profile.values()), 1)} ppm")
931
- print(f"EC: {self.calculate_ec()} mS/cm")
932
-
933
- print("\nРЕКОМЕНДУЕМЫЕ УДОБРЕНИЯ:")
934
- fert_table = []
935
- for fert, data in self.results.items():
936
- adds = [f"+{k}: {v:.1f} ppm" for k, v in data.items() if k.startswith('внесет')]
937
- fert_table.append([
938
- fert,
939
- round(data['граммы'], 3),
940
- data['миллиграммы'],
941
- round(data['вклад в EC'], 3),
942
- "\n".join(adds)
943
- ])
944
- print(tabulate(fert_table,
945
- headers=["Удобрение", "Граммы", "Миллиграммы", "EC (мСм/см)", "Добавит"]))
946
-
947
- print("\nОСТАТОЧНЫЙ ДЕФИЦИТ:")
948
- deficit = {
949
- k: round(self.target_profile[k] - self.actual_profile[k], 1)
950
- for k in self.target_profile
951
- if abs(self.target_profile[k] - self.actual_profile[k]) > 0.1
952
- }
953
- if deficit:
954
- for el, val in deficit.items():
955
- print(f" {el}: {val} ppm")
956
- else:
957
- print(" Все элементы покрыты полностью")
958
- except Exception as e:
959
- print(f"Ошибка при выводе отчёта: {str(e)}")
960
- raise
961
 
962
  if __name__ == "__main__":
963
  try:
 
733
  def __init__(self, volume_liters=1.0):
734
  self.volume = volume_liters
735
  self.target_profile = BASE_PROFILE.copy()
736
+ self.fertilizers = {
737
+ name: Composition.from_dict({name: content})
738
+ for name, content in NUTRIENT_CONTENT_IN_FERTILIZERS.items()
739
+ }
740
  self.total_ec = 0.0
741
  self.best_solution = None
742
  self.min_difference = float('inf')
743
+ self.max_recursion_depth = 1000
744
  self.current_depth = 0
 
745
 
746
  # Расчёт азота
747
  total_parts = NO3_RATIO + NH4_RATIO
748
  self.target_profile['N (NO3-)'] = TOTAL_NITROGEN * (NO3_RATIO / total_parts)
749
  self.target_profile['N (NH4+)'] = TOTAL_NITROGEN * (NH4_RATIO / total_parts)
750
 
751
+ # Целевой профиль как объект Composition
752
+ self.target_composition = Composition('Target Profile', list(self.target_profile.values()))
 
 
 
753
 
754
  def calculate(self):
755
  try:
 
761
  print("Оптимальная комбинация найдена!")
762
  return self.best_solution
763
  else:
764
+ print("Идеальное решение не найдено. Возвращаю лучшее найденное решение.")
765
+ return self.best_solution or {"error": "Не удалось найти подходящую комбинацию"}
766
 
767
  except Exception as e:
768
  print(f"Ошибка при расчёте: {str(e)}")
 
773
  if self.current_depth > self.max_recursion_depth:
774
  return False
775
 
776
+ # Текущий профиль как объект Composition
777
+ current_composition = Composition('Current Profile', list(self.actual_profile.values()))
778
+ current_diff = self._calculate_difference(current_composition)
779
+
780
  if current_diff < self.min_difference:
781
  self.min_difference = current_diff
782
  self.best_solution = {
 
786
  "difference": current_diff
787
  }
788
 
789
+ if current_diff < 1.0: # Допустимая погрешность
790
  return True
791
 
792
  # Пробуем добавлять удобрения с текущего индекса
793
+ for i in range(fertilizer_index, len(self.fertilizers)):
794
+ fert_name = list(self.fertilizers.keys())[i]
795
+ fert_composition = self.fertilizers[fert_name]
796
+
797
  # Проверяем, можно ли применить удобрение
798
+ if not self._can_apply_fertilizer(fert_composition):
799
  continue
800
 
801
  # Пробуем добавить удобрение с текущим шагом
 
809
  self._remove_fertilizer(fert_name, step)
810
 
811
  # Пробуем уменьшить шаг для более точного поиска
812
+ if step > 0.1 and current_diff > 5.0:
813
  if self._backtrack_search(i, step / 2):
814
  return True
815
 
816
  return False
817
 
818
+ def _can_apply_fertilizer(self, fert_composition):
819
  """Проверяет, можно ли применить удобрение без перебора"""
820
+ for element, content in zip(nutrients_stencil, fert_composition.vector):
821
  added_ppm = (1 * content * 1000) / self.volume
822
+ if self.actual_profile[element] + added_ppm > self.target_profile[element] + 1.0:
823
  return False
824
  return True
825
 
826
  def _apply_fertilizer(self, fert_name, amount):
827
  """Добавляет указанное количество удобрени��"""
828
+ fert_composition = self.fertilizers[fert_name]
829
+ scaled_composition = amount * fert_composition
830
+
831
  if fert_name not in self.results:
832
+ self.results[fert_name] = {
833
+ 'граммы': 0.0,
834
+ 'миллиграммы': 0,
835
+ 'вклад в EC': 0.0
836
+ }
837
+
838
  self.results[fert_name]['граммы'] += amount
839
  self.results[fert_name]['миллиграммы'] += int(amount * 1000)
840
+
841
+ for i, nutrient in enumerate(nutrients_stencil):
842
+ added_ppm = scaled_composition.vector[i] * 1000 / self.volume
843
+ self.actual_profile[nutrient] += added_ppm
 
 
 
 
 
 
844
 
845
  def _remove_fertilizer(self, fert_name, amount):
846
  """Удаляет указанное количество удобрения"""
847
+ fert_composition = self.fertilizers[fert_name]
848
+ scaled_composition = amount * fert_composition
849
+
850
  if fert_name in self.results:
851
  self.results[fert_name]['граммы'] -= amount
852
  self.results[fert_name]['миллиграммы'] -= int(amount * 1000)
853
+
854
+ for i, nutrient in enumerate(nutrients_stencil):
855
+ removed_ppm = scaled_composition.vector[i] * 1000 / self.volume
856
+ self.actual_profile[nutrient] -= removed_ppm
857
+
 
 
 
 
 
 
858
  if self.results[fert_name]['граммы'] <= 0.001:
859
  del self.results[fert_name]
860
 
861
+ def _calculate_difference(self, current_composition):
862
  """Вычисляет общее отклонение от целевого профиля"""
863
+ diff_vector = self.target_composition.vector - current_composition.vector
864
+ return np.sum(np.abs(diff_vector))
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
865
 
866
  def generate_report(self):
867
  """Генерация отчета о питательном растворе"""
868
  try:
869
+ actual_composition = Composition('Actual Profile', list(self.actual_profile.values()))
870
+ report = actual_composition.table(sparse=True, ref=self.target_composition)
 
 
 
 
 
 
 
 
 
 
871
  return report
872
  except Exception as e:
873
  print(f"Ошибка при выводе отчёта: {str(e)}")
874
  raise
875
 
876
+
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
877
 
878
  if __name__ == "__main__":
879
  try: