Skip to content

[Crop and Soil] Implement pasture crop type - #3163

Open
matthew7838 wants to merge 34 commits into
devfrom
pasture-crop-type
Open

[Crop and Soil] Implement pasture crop type#3163
matthew7838 wants to merge 34 commits into
devfrom
pasture-crop-type

Conversation

@matthew7838

@matthew7838 matthew7838 commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

Adds a pasture crop type for grazing, modeled after Tall Fescue, whose harvest deposits all cut biomass into the field as residue rather than removing it as harvested yield.

Context

Issue(s) closed by this pull request: closes #3131

What

  • Adds a new pasture crop configuration (perennial, based on the Tall Fescue hay parameters) that is available for use in crop rotations.
  • Adds a new optional crop-configuration flag, deposit_all_residue_at_harvest (default false).
  • When that flag is set, harvest operations collect no yield and deposit all cut biomass into the field as residue; no harvested crop is sent to feed storage.

Why

A pasture crop type is needed so animals can graze. For a grazed crop, forage removal is handled by grazing rather than by the harvest operation, so a "harvest" of a pasture should leave all cut biomass in the field as residue instead of removing it as stored yield.

How

  • Adds deposit_all_residue_at_harvest: bool = False to CropData, to the CropConfiguration TypedDict (as NotRequired), and to the crop-configuration schema in RUFAS/input/metadata/properties/default.json with "default": false (so the existing configurations auto-fill the field and are unaffected).
  • In CropManagement.manage_harvest, when the flag is set the crop is cut with collected_fraction=0.0 and _get_harvested_crop is skipped, so the method returns None and all cut biomass flows to the field as residue via the existing residue-transfer path.
  • Adds the pasture configuration to input/data/crop_configurations/default_crop_configs.json with the flag set to true.
  • Tightens the manage_harvest / manage_crop_harvest return type to HarvestedCrop | None (the method already returned None for kill-only operations) and widens the two field.py call-site annotations to match; this also removes a pre-existing mypy error.

Test plan

  • Run with updated unit tests.
  • SME output checks.

Input Changes

  • Adds an optional deposit_all_residue_at_harvest boolean (default false) to each crop configuration under crop_configuration_properties.
  • Adds a new pasture crop configuration to input/data/crop_configurations/default_crop_configs.json.
  • Existing crop configurations are unchanged; the new field auto-fills to false for all of them.

Output Changes

  • N/A

Filter

@github-actions
github-actions Bot force-pushed the pasture-crop-type branch from b7ae487 to c6865f4 Compare July 24, 2026 08:21
@github-actions
github-actions Bot force-pushed the pasture-crop-type branch from c6865f4 to 6d785a4 Compare July 24, 2026 08:21
@github-actions

Copy link
Copy Markdown
Contributor

Current Coverage: 99%

Mypy errors on pasture-crop-type branch: 1137
Mypy errors on dev branch: 1136
1 more errors on pasture-crop-type branch

@matthew7838
matthew7838 marked this pull request as ready for review July 24, 2026 08:34
@matthew7838
matthew7838 requested review from allisterakun, ew3361zh and morrowcj and removed request for ew3361zh July 24, 2026 09:15
@github-actions

Copy link
Copy Markdown
Contributor

Current Coverage: 99%

Mypy errors on pasture-crop-type branch: 1135
Mypy errors on dev branch: 1136
1 fewer errors on pasture-crop-type branch

@ew3361zh ew3361zh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!


actual = crop_manager.manage_harvest(harvest_op, "pasture_field", field_size, mock_time, soil_data)

# No yield is collected and no harvested crop is produced for feed storage.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think with how lengthy the docstring is, there's no need for inline comments (applies to the other test as well).

@github-actions

Copy link
Copy Markdown
Contributor

Current Coverage: 99%

Mypy errors on pasture-crop-type branch: 1135
Mypy errors on dev branch: 1136
1 fewer errors on pasture-crop-type branch

@matthew7838
matthew7838 removed the request for review from allisterakun July 28, 2026 17:06
@matthew7838 matthew7838 self-assigned this Jul 28, 2026

@morrowcj morrowcj left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • Adds a new optional crop-configuration flag, deposit_all_residue_at_harvest (default false).
  • When that flag is set, harvest operations collect no yield and deposit all cut biomass into the field as residue; no harvested crop is sent to feed storage.

This is probably my bad, but isn't quite how I think this should work. Instead, when the pasture is "harvested" by grazers:

  1. Some portion of the yield (the part that would normally be collected by the harvest operation) gets "fed" directly to the herd/cow. So, the yield bypasses the storage operations but becomes a feed operation.
  2. Then the remaining portion of the yield becomes residue and gets applied to the field (as currently occurs, but at a higher rate)

The efficiency of grazers is probably not the same as mechanical harvesting, so we should tweak the parameter(s) that control the ratio of collected yield to residue (maybe just the harvest_index unless there is a "harvest efficiency" parameter that I'm not aware of). Cattle grazers have a harvest efficiency of about 25% for season-long grazing (https://www.jstor.org/stable/40802661), meaning they consume about 25% of the above ground biomass. The efficiency for rotational grazing is about 40%.

@ew3361zh

Copy link
Copy Markdown
Collaborator

@matthew7838 do you mind retagging me for review once @morrowcj's changes have been implemented? I think it's a significant enough difference where a second look from the dev side might be helpful.

@github-actions

Copy link
Copy Markdown
Contributor

Current Coverage: 99%

Mypy errors on pasture-crop-type branch: 1134
Mypy errors on dev branch: 1135
1 fewer errors on pasture-crop-type branch

@matthew7838
matthew7838 requested a review from morrowcj July 30, 2026 04:38
@github-actions

Copy link
Copy Markdown
Contributor

Current Coverage: 99%

Mypy errors on pasture-crop-type branch: 1134
Mypy errors on dev branch: 1135
1 fewer errors on pasture-crop-type branch

@morrowcj morrowcj left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor change to the default grazing_harvest_efficiency value and a few other minor suggestions. Otherwise, LGTM.

Comment thread input/data/crop_configurations/default_crop_configs.json Outdated
Comment thread input/data/crop_configurations/default_crop_configs.json
Comment on lines +396 to +408
@property
def is_grazed(self) -> bool:
"""
Checks whether this crop is grazed rather than mechanically harvested.

Returns
-------
bool
True if a grazing harvest efficiency is given, False otherwise.

"""
return self.grazing_harvest_efficiency is not None

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wonder if this type of flag should be set by the (eventual) grazing harvest operation rather than by the crop configuration. For example, what if someone wanted to graze a pasture in one year, and then let it grow un-grazed the second year and machine harvest it for hay at the end of that season?

Comment thread RUFAS/input/metadata/properties/default.json Outdated
matthew7838 and others added 4 commits July 31, 2026 13:30
Co-authored-by: Clay Morrow <25436787+morrowcj@users.noreply.github.com>
Co-authored-by: Clay Morrow <25436787+morrowcj@users.noreply.github.com>
@github-actions
github-actions Bot force-pushed the pasture-crop-type branch from fcaa917 to 4842983 Compare July 31, 2026 04:32
@matthew7838
matthew7838 requested a review from ew3361zh July 31, 2026 04:32
@github-actions
github-actions Bot force-pushed the pasture-crop-type branch from 4842983 to cb3b684 Compare July 31, 2026 04:32
@github-actions
github-actions Bot force-pushed the pasture-crop-type branch from cb3b684 to 3b99409 Compare July 31, 2026 04:33
@github-actions

Copy link
Copy Markdown
Contributor

Current Coverage: 99%

Mypy errors on pasture-crop-type branch: 1134
Mypy errors on dev branch: 1135
1 fewer errors on pasture-crop-type branch

2 similar comments
@github-actions

Copy link
Copy Markdown
Contributor

Current Coverage: 99%

Mypy errors on pasture-crop-type branch: 1134
Mypy errors on dev branch: 1135
1 fewer errors on pasture-crop-type branch

@github-actions

Copy link
Copy Markdown
Contributor

Current Coverage: 99%

Mypy errors on pasture-crop-type branch: 1134
Mypy errors on dev branch: 1135
1 fewer errors on pasture-crop-type branch

"name": "pasture",
"plant_category": "perennial",
"is_nitrogen_fixer": false,
"grazing_harvest_efficiency": 0.50,

@morrowcj morrowcj Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this parameter redundant with CropManagement.harvest_efficiency?

@github-actions

github-actions Bot commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Current Coverage: 99%

Mypy errors on pasture-crop-type branch: 1138
Mypy errors on dev branch: 1135
3 more errors on pasture-crop-type branch

@ew3361zh ew3361zh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, not sure where the extra mypy errors are coming from looking at the code but probably somewhere that wasn't previously expecting a None type for HarvestedCrop.

If heat unit scheduling is used for harvesting.
harvest_efficiency : float, default 1.0
Efficiency of the harvest operation: the proportion of the cut biomass that is extracted from the field
(unitless; [0, 1]).

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
(unitless; [0, 1]).
(unitless; [0.0, 1.0]).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Soil & Crop]Implement a "pasture" crop type for grazing

3 participants