Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
@@ -729,6 +729,124 @@ EC_COEFFICIENTS = {
|
|
729 |
|
730 |
|
731 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
732 |
class NutrientCalculator:
|
733 |
def __init__(self, volume_liters=1.0):
|
734 |
self.volume = volume_liters
|
@@ -751,6 +869,7 @@ class NutrientCalculator:
|
|
751 |
# Целевой профиль как объект Composition
|
752 |
self.target_composition = Composition('Target Profile', list(self.target_profile.values()))
|
753 |
|
|
|
754 |
def calculate(self):
|
755 |
try:
|
756 |
self.actual_profile = {k: 0.0 for k in self.target_profile}
|
|
|
729 |
|
730 |
|
731 |
|
732 |
+
# Список питательных веществ
|
733 |
+
nutrients_stencil = [
|
734 |
+
'N (NO3-)',
|
735 |
+
'N (NH4+)',
|
736 |
+
'P',
|
737 |
+
'K',
|
738 |
+
'Mg',
|
739 |
+
'Ca',
|
740 |
+
'S',
|
741 |
+
'Fe',
|
742 |
+
'Zn',
|
743 |
+
'B',
|
744 |
+
'Mn',
|
745 |
+
'Cu',
|
746 |
+
'Mo',
|
747 |
+
]
|
748 |
+
|
749 |
+
|
750 |
+
class Composition:
|
751 |
+
def __init__(self, name='', vector=None):
|
752 |
+
self.name = name
|
753 |
+
if vector is None:
|
754 |
+
self.vector = np.zeros(len(nutrients_stencil))
|
755 |
+
else:
|
756 |
+
if len(vector) != len(nutrients_stencil):
|
757 |
+
raise ValueError(f"Vector length ({len(vector)}) does not match "
|
758 |
+
f"nutrients stencil length ({len(nutrients_stencil)}).")
|
759 |
+
self.vector = np.array(vector)
|
760 |
+
|
761 |
+
@classmethod
|
762 |
+
def from_dict(cls, composition_dict):
|
763 |
+
if not composition_dict:
|
764 |
+
raise ValueError("Empty composition dictionary provided.")
|
765 |
+
name, nutrients_dict = tuple(composition_dict.items())[0]
|
766 |
+
vector = np.zeros(len(nutrients_stencil))
|
767 |
+
for i, nutrient in enumerate(nutrients_stencil):
|
768 |
+
if nutrient in nutrients_dict:
|
769 |
+
vector[i] = nutrients_dict[nutrient]
|
770 |
+
return cls(name, vector)
|
771 |
+
|
772 |
+
def __add__(self, composition):
|
773 |
+
if not isinstance(composition, Composition):
|
774 |
+
raise TypeError("Can only add Composition objects.")
|
775 |
+
name = f'{self.name} + {composition.name}'
|
776 |
+
vector = self.vector + composition.vector
|
777 |
+
return Composition(name, vector)
|
778 |
+
|
779 |
+
def __neg__(self):
|
780 |
+
return Composition(f'- ({self.name})', -self.vector)
|
781 |
+
|
782 |
+
def __sub__(self, composition):
|
783 |
+
if not isinstance(composition, Composition):
|
784 |
+
raise TypeError("Can only subtract Composition objects.")
|
785 |
+
name = f'{self.name} - {composition.name}'
|
786 |
+
vector = self.vector - composition.vector
|
787 |
+
return Composition(name, vector)
|
788 |
+
|
789 |
+
def __eq__(self, other):
|
790 |
+
if not isinstance(other, Composition):
|
791 |
+
return False
|
792 |
+
return np.all(self.vector == other.vector)
|
793 |
+
|
794 |
+
def __repr__(self):
|
795 |
+
return self.table()
|
796 |
+
|
797 |
+
def __str__(self):
|
798 |
+
return f'{self.name} :: ' + ', '.join(
|
799 |
+
f'{nutrient}: {amount_ppm:.2f}'
|
800 |
+
for nutrient, amount_ppm
|
801 |
+
in zip(nutrients_stencil, 10**6 * self.vector)
|
802 |
+
)
|
803 |
+
|
804 |
+
def __rmul__(self, number):
|
805 |
+
if not isinstance(number, (int, float)):
|
806 |
+
raise TypeError("Can only multiply by a scalar.")
|
807 |
+
name = f'{number} * ({self.name})'
|
808 |
+
vector = self.vector * number
|
809 |
+
return Composition(name, vector)
|
810 |
+
|
811 |
+
def __len__(self):
|
812 |
+
return len(self.vector)
|
813 |
+
|
814 |
+
def as_dict(self):
|
815 |
+
nutrients_dict = {
|
816 |
+
nutrient: float(value)
|
817 |
+
for nutrient, value in zip(nutrients_stencil, self.vector)
|
818 |
+
if value != 0
|
819 |
+
}
|
820 |
+
return {self.name: nutrients_dict}
|
821 |
+
|
822 |
+
def table(self, sparse=True, ref=None, tablefmt='simple'):
|
823 |
+
description = f'Composition: {self.name}'
|
824 |
+
nutrients = np.array(nutrients_stencil)
|
825 |
+
vector = self.vector
|
826 |
+
if ref is not None:
|
827 |
+
if not isinstance(ref, Composition):
|
828 |
+
raise TypeError("Reference must be a Composition object.")
|
829 |
+
vector_ref = ref.vector
|
830 |
+
else:
|
831 |
+
vector_ref = np.zeros(len(nutrients_stencil))
|
832 |
+
if sparse:
|
833 |
+
mask_nonzero = (vector != 0) | (vector_ref != 0)
|
834 |
+
nutrients = nutrients[mask_nonzero]
|
835 |
+
vector = vector[mask_nonzero]
|
836 |
+
vector_ref = vector_ref[mask_nonzero]
|
837 |
+
table_dict = {
|
838 |
+
'Nutrient': nutrients,
|
839 |
+
'Ratio': vector,
|
840 |
+
'Amount mg/kg': 10**6 * vector,
|
841 |
+
}
|
842 |
+
if ref is not None:
|
843 |
+
description += f'\nReference: {ref.name}'
|
844 |
+
table_dict['Diff mg/kg'] = 10**6 * (vector - vector_ref)
|
845 |
+
table = tabulate(table_dict, headers='keys', tablefmt=tablefmt)
|
846 |
+
return '\n\n'.join((description, table))
|
847 |
+
|
848 |
+
|
849 |
+
# Ваш калькулятор удобрений
|
850 |
class NutrientCalculator:
|
851 |
def __init__(self, volume_liters=1.0):
|
852 |
self.volume = volume_liters
|
|
|
869 |
# Целевой профиль как объект Composition
|
870 |
self.target_composition = Composition('Target Profile', list(self.target_profile.values()))
|
871 |
|
872 |
+
|
873 |
def calculate(self):
|
874 |
try:
|
875 |
self.actual_profile = {k: 0.0 for k in self.target_profile}
|