microsoft / farmvibes-ai

FarmVibes.AI: Multi-Modal GeoSpatial ML Models for Agriculture and Sustainability

Home Page:https://microsoft.github.io/farmvibes-ai/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Error with any ADMA related notebook execution. Why is it looking for harvest data?

chetan2309 opened this issue · comments

Topic

Notebook

Ask away!

Hi Team,

I am running ADMA related notebooks; I actually ran two of the notebooks: -

  • azure_data_manager_for_agriculture_example
  • nutrients_using_classification_admag

Both notebooks are encountering a similar sort of error "No harvest found with parameters: ". It seems like that underlying ADMA's Harvest API is returning zero results.

  • I have a script a created data but it seems like underlying ADMA API is returning zero results for same intersection geometry. I am also going to have a chat with someone from ADMA team about this to see if there is any error on our behalf while trying to create this data. Because right now all we have is some data ingested through scripts.
{'admag_seasonal_field': RunDetails(start_time=datetime.datetime(2024, 5, 2, 12, 51, 56, 734280), submission_time=datetime.datetime(2024, 5, 2, 12, 51, 56, 642432), end_time=datetime.datetime(2024, 5, 2, 12, 52, 16, 413337), reason='RuntimeError: Failed to run op admag_seasonal_field in workflow run id 7aacee14-fe77-41da-869e-ce691e8b2213 for input with message id 00-7aacee14fe7741da869ece691e8b2213-6a1ad0f98a272a8b-01. Error description: <class \'RuntimeError\'>:  ValueError(f"No harvest found with parameters: {operation_params}")\nValueError: No harvest found with parameters: {\'party_id\': \'Farmer2\', \'intersects_with_geometry\': {\'type\': \'Polygon\', \'coordinates\': [[[-92.12573593664294, 46.78383790254702], [-92.12573593664294, 46.78250921749722], [-92.12433358355645, 46.78250921749722], [-92.12433358355645, 46.78383790254702], [-92.12573593664294, 46.78383790254702]]]}, \'min_start_operation\': \'2023-02-15T06:46:35Z\', \'max_end_operation\': \'2023-03-15T06:46:35Z\', \'associated_resource\': {\'type\': \'SeasonalField\', \'id\': \'SummerField-Corn-KK-May2\'}}\n.', status=<RunStatus.failed: 'failed'>, subtasks=[{'status': 'failed', 'subtasks': None, 'start_time': '2024-05-02T12:51:56.734280', 'submission_time': '2024-05-02T12:51:56.642432', 'end_time': '2024-05-02T12:52:16.413168', 'reason': 'RuntimeError: Failed to run op admag_seasonal_field in workflow run id 7aacee14-fe77-41da-869e-ce691e8b2213 for input with message id 00-7aacee14fe7741da869ece691e8b2213-6a1ad0f98a272a8b-01. Error description: <class \'RuntimeError\'>:  ValueError(f"No harvest found with parameters: {operation_params}")\nValueError: No harvest found with parameters: {\'party_id\': \'Farmer2\', \'intersects_with_geometry\': {\'type\': \'Polygon\', \'coordinates\': [[[-92.12573593664294, 46.78383790254702], [-92.12573593664294, 46.78250921749722], [-92.12433358355645, 46.78250921749722], [-92.12433358355645, 46.78383790254702], [-92.12573593664294, 46.78383790254702]]]}, \'min_start_operation\': \'2023-02-15T06:46:35Z\', \'max_end_operation\': \'2023-03-15T06:46:35Z\', \'associated_resource\': {\'type\': \'SeasonalField\', \'id\': \'SummerField-Corn-KK-May2\'}}\n.'}])}

However at the same time, A Few observations/questions that I have:-

  • What I have failed to understand is while trying to calculate spectral indices or nutrients classification why it has to access Harvest Data?
  • Is there any way in the code where I could see what calls this workflow is making? The documentation is limited in explaining what all data do I need to have before executing this notebook.
  • Based on my limited understanding of ADMA, I was hoping for that to calculate either NDVI or Nutrients classification it will need geometry of the field/season field.
  • Is it not possible to calculate many of these indices such as NDVI, NDWI etc by geometry using Sentinel imagery in these cases also?

P.S. - Is there any write up that I can read to solidify my understanding? I come from an application development background with some domain knowledge.

Regards,
Chetan

Hi Chetan,

Thank you for testing this. I apologize for the challenges you're facing in getting this done. Let me try to explain the integration between ADMA and FarmVibes.AI.

FarmVibes.AI uses ADMA as a data provider to generate a SeasonalFieldInformation class. This class represents the activities in a field during a season, from the initial planting process (time_range[0]) to the latest harvest (time_range[1]). These times are used to create the time_range field in the SeasonalFieldInformation. It also contains important information about the field for this season such as crop_name, crop_type, fertilizers, etc. Therefore, every time we use ADMA to create a SeasonalFieldInformation object, we need at least the crop start (planting) and end information (harvest) to create the time_range field.

With regard to the HeatMap notebooks, ADMA is one of the ways to obtain field information for execution (not the only one).
I agree that harvest data would not be necessary to run the data_processing/heatmap/classification workflow. That's why we also provide the farm_ai/agriculture/heatmap_using_classification workflow (see nutrients_using_classification.ipynb) which uses an ExternalReferenceList object to provide the field geojson. In this case, no harvest information is required.

Regarding the indexes, this notebook might be helpful. Currently, the spectral_indices notebook support all the indexes from the Awesome Indexes project.

We also going to update our documentation to make the FarmVibes.AI ADMA relationship more clear. Thanks again to pointing this.

Best Regards
Bruno

Hi Chetan,

Thank you for testing this. I apologize for the challenges you're facing in getting this done. Let me try to explain the integration between ADMA and FarmVibes.AI.

FarmVibes.AI uses ADMA as a data provider to generate a SeasonalFieldInformation class. This class represents the activities in a field during a season, from the initial planting process (time_range[0]) to the latest harvest (time_range[1]). These times are used to create the time_range field in the SeasonalFieldInformation. It also contains important information about the field for this season such as crop_name, crop_type, fertilizers, etc. Therefore, every time we use ADMA to create a SeasonalFieldInformation object, we need at least the crop start (planting) and end information (harvest) to create the time_range field.

I spent almost entire day today to work through the example. The reason API was giving empty response was because of the fields operationStartDateTime and operationEndDateTime which were used by min_start_operation and max_end_operation in admag_client.py file. After a lot of hit and trial I probably got that these dates have to match with season's start and end date, those dates we used in creating season. It made sense. Then we had to add geometry property to harvest-data. We had to same thing for planting data also.

Now, I am finally getting this error.

{'admag_seasonal_field': RunDetails(start_time=datetime.datetime(2024, 5, 3, 18, 30, 2, 835748), submission_time=datetime.datetime(2024, 5, 3, 18, 30, 2, 800030), end_time=datetime.datetime(2024, 5, 3, 18, 30, 23, 756600), reason="RuntimeError: Failed to run op admag_seasonal_field in workflow run id a1b91888-5fb2-4184-8a9a-a9826b55448c for input with message id 00-a1b918885fb241848a9aa9826b55448c-32524ba44fd33432-01. Error description: <class 'RuntimeError'>:  ValueError(\nValueError: Harvest does not have gfsrt property. Please check harvest properties with id=35d21af2-4934-4324-a688-4d92a7ca0024 in Admag. havest['properties']['gfsrt'] = True, means the crop is grain.\n.", status=<RunStatus.failed: 'failed'>, subtasks=[{'start_time': '2024-05-03T18:30:02.835748', 'submission_time': '2024-05-03T18:30:02.800030', 'end_time': '2024-05-03T18:30:23.756443', 'reason': "RuntimeError: Failed to run op admag_seasonal_field in workflow run id a1b91888-5fb2-4184-8a9a-a9826b55448c for input with message id 00-a1b918885fb241848a9aa9826b55448c-32524ba44fd33432-01. Error description: <class 'RuntimeError'>:  ValueError(\nValueError: Harvest does not have gfsrt property. Please check harvest properties with id=35d21af2-4934-4324-a688-4d92a7ca0024 in Admag. havest['properties']['gfsrt'] = True, means the crop is grain.\n.", 'status': 'failed', 'subtasks': None}])}

I can provide the script that I am using, but it would be great if you could share some data-generation scripts that can generate all data that would satisfy all the needs of the system. Otherwise, I'm ready to hang-up my boots and may be pick-up in future when there are more details around this.

With regard to the HeatMap notebooks, ADMA is one of the ways to obtain field information for execution (not the only one). I agree that harvest data would not be necessary to run the data_processing/heatmap/classification workflow. That's why we also provide the farm_ai/agriculture/heatmap_using_classification workflow (see nutrients_using_classification.ipynb) which uses an ExternalReferenceList object to provide the field geojson. In this case, no harvest information is required.

Thanks for reference to these notebooks, I can definitely work with these: -

  1. So, I believe this would mean we would have to write custom workflows to get data or provide geojson file for let say any new field created in the system and call this workflow? Do you have any reference on how to do so?
  2. Since we are going to consume these generated indices as PNG files in our system to show them calculated data, do we have any workflows that can save these indices back to customer's blob storage?

Regarding the indexes, this notebook might be helpful. Currently, the spectral_indices notebook support all the indexes from the Awesome Indexes project.

We also going to update our documentation to make the FarmVibes.AI ADMA relationship more clear. Thanks again to pointing this.

Best Regards Bruno

P.S - I am attaching here data generation script. Would appreciate if you can share yours.
adma-llm-data-gen.zip

Best Regards,
Chetan

Hi @chetan2309,

Thanks a lot for providing the code. I am sorry that you had to spend such a long time on this. I believe some examples from the ADMA data we have might be useful in resolving the issue you're currently facing.

Seasonal Field
{'farmId': '0861e6b5-88a9-4021-b94e-c4e9d6094b80-18', 'fieldId': '04b1d9f6-7444-4df5', 'name': 'Alfalfa', 'description': 'annual', 'status': 'Active', 'seasonId': '333a7fcd-641a-4110', 'cropId': '09c27f6f-1478-4720', 'cropVarietyIds': None, 'avgYieldValue': None, 'avgYieldUnit': None, 'avgSeedPopulationValue': None, 'avgSeedPopulationUnit': None, 'properties': None, 'geometry': {'type': 'MultiPolygon', 'coordinates': []}}


Harvest

{'source': 'baseline', 'name': None, 'description': None, 'status': 'Active', 'operationStartDateTime': '2000/09/05', 'operationEndDateTime': '2000/09/05', 'totalYield': {'unit': 'tons', 'value': 39}, 'operationModifiedDateTime': None, 'area': None, 'avgYield': None, 'totalWetMass': None, 'avgWetMass': None, 'avgMoisture': None, 'avgSpeed': None, 'harvestProductDetails': None, 'properties': {'gfsrt': 'True', 'strawStoverHayRemoval': '0'}, 'geometry': {'type': 'MultiPolygon', 'coordinates': []}, 'associatedResource': {'type': 'SeasonalField', 'id': 'fe5cc763-3a1e-4017'}}


Tillage
{'source': 'baseline', 'name': 'Reduced Tillage', 'description': None, 'status': 'Active', 'operationStartDateTime': '2000/01/01', 'operationEndDateTime': '2000/01/01', 'operationModifiedDateTime': None, 'tillageDepth': None, 'tillagePressure': None, 'area': None, 'properties': None, 'geometry': {'type': 'MultiPolygon', 'coordinates': []}, 'associatedResource': {'type': 'SeasonalField', 'id': 'fe5cc763-3a1e-4017'}}


Fertilizers

{'source': 'baseline', 'operationStartDateTime': '2000/01/01', 'operationEndDateTime': '2000/01/01', 'totalMaterial': {'unit': 'tons/ac', 'value': 5.0}, 'name': 'Ammonium Nitrate (34-0-0)', 'properties': {'eep': 'None', 'totalNitrogen': 4.0, 'method': 'Surface Band / Sidedress'}, 'geometry': {'type': 'MultiPolygon', 'coordinates': []}, 'associatedResource': {'type': 'SeasonalField', 'id': 'fe5cc763-3a1e-4017'}}


OMAD - Organic Amendments

{'source': 'baseline', 'operationStartDateTime': '2000/03/01', 'operationEndDateTime': '2000/03/01', 'totalMaterial': {'unit': 'tons/ac', 'value': 10.0}, 'name': None, 'properties': {'date': '2000/03/01', 'type': 'Beef Manure, Solid', 'amount': 10.0, 'percentN': 0.6, 'CNratio': 1.2}, 'geometry': {'type': 'MultiPolygon', 'coordinates': []}, 'associatedResource': {'type': 'SeasonalField', 'id': 'fe5cc763-3a1e-4017'}}      

Just make sure to replace the geometry coordinates and ids with your actual values (The ids here are fake).

Please, let me know if this works for you.

Att

@brsilvarec - thanks for your reply, I will try with this data tomorrow. However, since one of the primary use case w.r.t ADMA integration is to be able to see certain indices in advance at field level I am afraid this may not be the right workflow or may be develop a custom workflow with custom needs?

W.r.t spectral indices notebook - Since we are going to consume these generated indices as PNG/TIFF/JPEG files in our application to show users with calculated indices, do we have any custom workflows that can save these indices back to customer's blob storage?

There are 2 approaches I could think of:-

  1. Access these images in VM .cache through an API for a user, if yes what is the way?
  2. Write a custom workflow operation that could write back these images in a folder like hierarchy in Azure blob storage.

Could you let me know which approach will work best along with any examples?

Regards,
Chetan

@brsilvarec - there still seems to be error in the data.

  1. cropVarietyIds, avgYieldValue, avgYieldUnit avgSeedPopulationValue, avgSeedPopulationUnit are invalid fields as per the newest model, can you verify from your end too.
  2. Below is my final payload to create seasons field data
payload = json.dumps({
    "farmId": farm_id,
    "fieldId": field_id,
    "name": field_name,
    "description": "These are seasonal fields created through data ingestion.",
    'status': 'Active',
    "cropId": crop_id,
    "seasonId": "summer",
    #'cropVarietyIds': None,
    #'avgYieldValue': None,
    #'avgYieldUnit': None,
    #'avgSeedPopulationValue': None,
    #'avgSeedPopulationUnit': None,
    "properties": {
        "year": year,
        "crop": crop,
        "boundary_size": area,
        "planting_date": planting_date,
        "yield": avgYield,
        "harvest_date": datetime.strptime(harvest_date,  "%d-%b-%y").strftime("%m-%d-%Y")
    },
    "geometry": polygon,
  })

I am getting below error - Any clue how the code is constructing field_info data? I am unable to find this in farmvibes code repo. Please help

{'admag_seasonal_field': RunDetails(start_time=datetime.datetime(2024, 5, 7, 8, 21, 15, 810632), submission_time=datetime.datetime(2024, 5, 7, 8, 21, 15, 589626), end_time=datetime.datetime(2024, 5, 7, 8, 21, 36, 325985), reason='RuntimeError: Failed to run op admag_seasonal_field in workflow run id 1f5ca14c-87c2-4927-8741-4c47e4bc86b2 for input with message id 00-1f5ca14c87c2492787414c47e4bc86b2-2a7ea9c2f2c91b1a-01. Error description: <class 'RuntimeError'>: Traceback (most recent call last):\n File "/opt/conda/lib/python3.11/site-packages/vibe_agent/worker.py", line 142, in run_op\n return factory.build(spec).run(input, cache_info)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/opt/conda/lib/python3.11/site-packages/vibe_agent/ops.py", line 106, in run\n stac_results = self._call_validate_op(**items)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/opt/conda/lib/python3.11/site-packages/vibe_agent/ops.py", line 72, in _call_validate_op\n results = self.callback(**kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^\n File "/app/ops/admag/admag_seasonal_field_op.py", line 309, in get_admag_seasonal_field\n seasonal_field = self.get_season_field_data(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/app/ops/admag/admag_seasonal_field_op.py", line 301, in get_season_field_data\n properties=field_info["properties"],\n ~~~~~~~~~~^^^^^^^^^^^^^^\nKeyError: 'properties'\n.', status=<RunStatus.failed: 'failed'>, subtasks=[{'start_time': '2024-05-07T08:21:15.810632', 'submission_time': '2024-05-07T08:21:15.589626', 'end_time': '2024-05-07T08:21:36.325799', 'reason': 'RuntimeError: Failed to run op admag_seasonal_field in workflow run id 1f5ca14c-87c2-4927-8741-4c47e4bc86b2 for input with message id 00-1f5ca14c87c2492787414c47e4bc86b2-2a7ea9c2f2c91b1a-01. Error description: <class 'RuntimeError'>: Traceback (most recent call last):\n File "/opt/conda/lib/python3.11/site-packages/vibe_agent/worker.py", line 142, in run_op\n return factory.build(spec).run(input, cache_info)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/opt/conda/lib/python3.11/site-packages/vibe_agent/ops.py", line 106, in run\n stac_results = self._call_validate_op(**items)\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/opt/conda/lib/python3.11/site-packages/vibe_agent/ops.py", line 72, in _call_validate_op\n results = self.callback(**kwargs)\n ^^^^^^^^^^^^^^^^^^^^^^^\n File "/app/ops/admag/admag_seasonal_field_op.py", line 309, in get_admag_seasonal_field\n seasonal_field = self.get_season_field_data(\n ^^^^^^^^^^^^^^^^^^^^^^^^^^^\n File "/app/ops/admag/admag_seasonal_field_op.py", line 301, in get_season_field_data\n properties=field_info["properties"],\n ~~~~~~~~~~^^^^^^^^^^^^^^\nKeyError: 'properties'\n.', 'status': 'failed', 'subtasks': None}])}

Thanks @chetan2309 for checking on this.

I still see this attributes cropVarietyIds, avgYieldValue, avgYieldUnit avgSeedPopulationValue, avgSeedPopulationUnit in the document of Seasonal field. Which ADMAG version you're using?

See below url for the document we are using.
https://github.com/MicrosoftDocs/Azure-FarmBeats/blob/main/documentation/farm%20hierarchy/REST%20APIs/2021-07-31-preview/seasonal%20fields/1.%20create_update_seasonal_field.md

For the issue you're seeing with workflow, can you check the field object inserted in ADMAG with json shared below.
Field
{'farmId': '0861e6b5-88a9-4021-', 'name': 'Middle east fields', 'description': 'AI Agriculture Field Research', 'status': 'Active', 'properties': {'pre_1980': 'Irrigation (Pre 1980s)', 'crp_type': 'None', 'crp_start': '', 'crp_end': '', 'year_1980_2000': 'Irrigated: Annual Crops in Rotation', 'year_1980_2000_tillage': 'Intensive Tillage'}, 'geometry': {'type': 'MultiPolygon', 'coordinates': []}}

Also, in the json shared for seasonal field, I see planting date is mapped with seasonal field. In new version, planting has separate resource and it's mapped with seasonal field. Please see below json and make changes accordingly.
Planting
{'area': {'unit': 'acre', 'value': 0}, 'associatedResource': {'id': '7d60d569-9ce0-', 'type': 'SeasonalField'}, 'avgMaterial': {'unit': 'seedsperacre', 'value': 0}, 'avgPlantingRate': {'unit': 'seedsperacre', 'value': 0}, 'description': 'some description', 'geometry': {'type': 'MultiPolygon', 'coordinates': []}, 'name': 'Andrew Farm', 'operationEndDateTime': '2000/02/15T00:00:00Z', 'operationModifiedDateTime': '2000/02/15T00:00:00Z', 'operationStartDateTime': '2000/02/15T00:00:00Z', 'plantingProductDetails': [{'area': {'unit': 'acre', 'value': 0}, 'avgMaterial': {'unit': 'seedsperacre', 'value': 0}, 'productName': 'VAR1', 'totalMaterial': {'unit': 'seeds', 'value': 0}}], 'properties': {}, 'source': 'baseline', 'status': 'Active', 'totalMaterial': {'unit': 'seeds', 'value': 0}}

I am using latest version of ADMA 2023-11-01-preview, the version link you mentioned here is extremely dated. Please share your findings w.r.t latest version, for some operational reasons as we are on 2023-11-01-preview it will be impossible for us to use any prior version.

Seasonal fields latest version - is what I am using.

REgards,
Chetan

Sorry for the confusion. It's version "2023-11-01-preview" we have upgraded recently. I shared the wrong url.

These fields cropVarietyIds, avgYieldValue, avgYieldUnit avgSeedPopulationValue, avgSeedPopulationUnit are removed in recent upgrade, I see that. You can ignore them. It still exists in data load scripts we are using internally.

Will check and confirm you back if these fields causing the workflow to fail.

Still, little bit of confusion.

If you see my point no 2 above where I mentioned that I removed these fields to make workflow proceed forward but now I am blocked on -

File "/app/ops/admag/admag_seasonal_field_op.py", line 301, in get_season_field_data\n properties=field_info["properties"],\n ~~~~~~~~~~^^^^^^^^^^^^^^\nKeyError: 'properties'\n.',

Can you please check this code on your end to help identify the error? Because you can see payload that I have provided above. It clearly has properties field although in the example provided by your team it was null. Tried with null also, still same error.

The error you're seeing due to missing properties in Field. Can you check if field object having properties?

Field
{'farmId': '0861e6b5-88a9-4021-', 'name': 'Middle east fields', 'description': 'AI Agriculture Field Research', 'status': 'Active', 'properties': {'pre_1980': 'Irrigation (Pre 1980s)', 'crp_type': 'None', 'crp_start': '', 'crp_end': '', 'year_1980_2000': 'Irrigated: Annual Crops in Rotation', 'year_1980_2000_tillage': 'Intensive Tillage'}, 'geometry': {'type': 'MultiPolygon', 'coordinates': []}}

What exact fields is it looking for, if it is only looking for the Key -> "Properties" then I have. Because, as I understand you can put anything in properties bag? Right? I think my concern is also that error message is not descriptive enough.

def create_field(farm_id, field_id):
  # Step 3: Create a field.
  print(f"Creating or updating field with Id {field_id}...", end=" ", flush=True)
  url = base_url + f"parties/{party_id}/fields/{field_id}" + "?" + api_version
  payload = json.dumps(
    {
      "farmId": farm_id,
      "status": "Active",
      "name": "John Smith's Field",
      "description": "This is an awesome John's field",
      "source": "Data ingestion",
      "properties": {
        "Irrigated": "Yes",
        "RetailerId": "Retailer123"
      }
    }
  )
  print(url)
  print (make_request(url, payload).text)

It's strange. It's just looking for key "properties".

Can you share me what information you're getting if you do get on field_id you're inserting?