Prespecified Policies¶
Farmers and growers often operate using open-loop policies, or relatively simple closed loop policies subject to the observations avaiable to them.
pcse_gym.policies
provides a variety of configurable prespecified policies that might be used on a farm.
Using a Prespecified Policy¶
Using a prespecified policy in WOFOST-Gym usually arises when generating data for comparison with another policy, or on another farm. See Prespecified Policies for an example on how to call the policy to generate data.
Every prespecified policy assumes that the Envirnoment
is wrapped using a NPKDictActionWrapper
and NPKDictObservationWrapper
.
Using these wrappers makes it much easier to specify a policy, as it makes the actions and observations key-word accessible. A prespecified policy will
throw an error if the environment is not correctly wrapped.
Every policy has a __call()
function which takes in the crop observation. For example:
policy = policies.Below_N(args)
# Make Environment
env = utils.make_gym_env(args)
env = env.NPKDictActionWrapper(env)
env = env.NPKDictObservationWrapper(env)
obs, _ = env.reset()
action = policy(obs)
will return the action to be taken given the current observation.
Important
Every policy requires that specific observation variables be availble in the obs
dictionary. If an environment
does not have these variables, an error will be thrown. For information on configuring environment output, see Environment Configuration.
Creating a Prespecified Policy¶
It is likely that a prespecified policy needs to be created to fit the specific use case.
Every prespecified policy should have the following header:
class Threshold_N(Policy):
"""Policy applying a small amount of Nitrogen at a given interval
"""
required_vars = ["TOTN"]
def __init__(self, env: gym.Env, args):
"""Initialize the :class:`Interval_N`.
Args:
env: The Gymnasium Environment
required_vars: list of required state space variables
"""
super().__init__(env, required_vars=self.required_vars)
The above policy requires the "TOTN"
variable as output from the environment.
args
is usually an instance of gen_data.DataArgs
which contains variables for:
threshold
which controls the total amount of fertilizer than can be applied, or the limit below which fertilizer will be applied.amount
which controls the amount of fertilizer or water applied.interval
which controls how often fertilizer or water is applied.
After the header, the _get_action(self, obs)
function should be created. For example, suppose we want to apply nitrogen fertilizer as long as a total threshold has not been reached.
The function to do this would look like:
def _get_action(self, obs):
"""Returns an action that applies Nitrogen until a threshold is met
"""
if obs['TOTN'] > self.threshold:
return {'n': 0, 'p': 0, 'k': 0, 'irrig':0 }
else:
return {'n': self.amount, 'p': 0, 'k': 0, 'irrig':0 }
The TOTN
observation records the total amount of Nitrogen applied. If it is above some threshold, no Nitrogen will be applied,
as shown by the {'n': 0, 'p': 0, 'k': 0, 'irrig':0 }
block.
Every output of _get_action(obs)
should be a dictionary with the keys 'n'
, 'p'
, 'k'
, and 'irrig'
which give the amount of Nitrogen, Phosphorous, Potassium, and water to be applied.
The amount of fertilizer or water that is actually applied is controlled by the --npk.fert-amount
and npk.irrig-amount
parameters.
For more information see Environment Configuration.