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:
thresholdwhich controls the total amount of fertilizer than can be applied, or the limit below which fertilizer will be applied.amountwhich controls the amount of fertilizer or water applied.intervalwhich 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.