/// <summary> /// Method to set defaults from /// </summary> public void SetDefaults() { //Iterate through properties foreach (var property in GetType().GetProperties()) { //Iterate through attributes of this property foreach (Attribute attr in property.GetCustomAttributes(true)) { //does this property have [DefaultValueAttribute]? if (attr is System.ComponentModel.DefaultValueAttribute) { //So lets try to load default value to the property System.ComponentModel.DefaultValueAttribute dv = (System.ComponentModel.DefaultValueAttribute)attr; try { object result = property.GetValue(this, null); if (result is null) { //Is it an array? if (property.PropertyType.IsArray) { property.SetValue(this, dv.Value, null); } else { //Use set value for.. not arrays property.SetValue(this, dv.Value, null); } } } catch (Exception ex) { Summary.WriteWarning(this, ex.Message); //eat it... Or maybe Debug.Writeline(ex); } } } } }
private void OnSimulationCommencing(object sender, EventArgs e) { // check payment interval > 0 if (PaymentInterval <= 0) { Summary.WriteWarning(this, String.Format("Overhead payment interval must be greater than 1 ({0})", this.Name)); throw new Exception(String.Format("Invalid payment interval supplied for overhead {0}", this.Name)); } if (MonthDue >= Clock.StartDate.Month) { NextDueDate = new DateTime(Clock.StartDate.Year, MonthDue, Clock.StartDate.Day); } else { NextDueDate = new DateTime(Clock.StartDate.Year, MonthDue, Clock.StartDate.Day); while (Clock.StartDate > NextDueDate) { NextDueDate = NextDueDate.AddMonths(PaymentInterval); } } }
/// <summary>Removes plant material simulating a graze event.</summary> /// <param name="type">The type of amount being defined (SetResidueAmount or SetRemoveAmount)</param> /// <param name="amount">The DM amount (g/m2)</param> /// <param name="summary">Optional summary object.</param> public void RemoveBiomass(string type, double amount, ISummary summary = null) { double harvestableWt = Material.Sum(m => m.Consumable.Wt); if (!MathUtilities.FloatsAreEqual(harvestableWt, 0.0)) { // Get the amount required to remove double amountRequired; if (type.ToLower() == "setresidueamount") { // Remove all DM above given residual amount amountRequired = Math.Max(0.0, harvestableWt - amount); } else if (type.ToLower() == "setremoveamount") { // Remove a given amount amountRequired = Math.Max(0.0, amount); } else { throw new Exception("Type of amount to remove on graze not recognized (use \'SetResidueAmount\' or \'SetRemoveAmount\'"); } // Get the actual amount to remove double amountToRemove = Math.Max(0.0, Math.Min(amountRequired, harvestableWt)); // Do the actual removal if (!MathUtilities.FloatsAreEqual(amountToRemove, 0, 0.0001)) { RemoveBiomass(amountToRemove); } } else { summary.WriteWarning(forageModel as IModel, "Could not graze due to lack of DM available"); } }
/// <summary> /// Create the individual ruminant animals using the Cohort parameterisations. /// </summary> /// <returns></returns> public List <Ruminant> CreateIndividuals() { List <Ruminant> Individuals = new List <Ruminant>(); RuminantType parent = this.Parent.Parent as RuminantType; // get Ruminant Herd resource for unique ids RuminantHerd ruminantHerd = Resources.RuminantHerd(); if (Number > 0) { for (int i = 1; i <= Number; i++) { object ruminantBase = null; if (this.Gender == Sex.Male) { ruminantBase = new RuminantMale(); } else { ruminantBase = new RuminantFemale(); } Ruminant ruminant = ruminantBase as Ruminant; ruminant.ID = ruminantHerd.NextUniqueID; ruminant.BreedParams = parent; ruminant.Breed = parent.Breed; ruminant.HerdName = parent.Name; ruminant.Gender = Gender; ruminant.Age = Age; ruminant.SaleFlag = HerdChangeReason.None; if (Suckling) { ruminant.SetUnweaned(); } if (Sire) { if (this.Gender == Sex.Male) { RuminantMale ruminantMale = ruminantBase as RuminantMale; ruminantMale.BreedingSire = true; } else { Summary.WriteWarning(this, "Breeding sire switch is not valid for individual females"); } } double u1 = ZoneCLEM.RandomGenerator.NextDouble(); double u2 = ZoneCLEM.RandomGenerator.NextDouble(); double randStdNormal = Math.Sqrt(-2.0 * Math.Log(u1)) * Math.Sin(2.0 * Math.PI * u2); ruminant.Weight = Weight + WeightSD * randStdNormal; ruminant.PreviousWeight = ruminant.Weight; if (this.Gender == Sex.Female) { RuminantFemale ruminantFemale = ruminantBase as RuminantFemale; ruminantFemale.DryBreeder = true; ruminantFemale.WeightAtConception = ruminant.Weight; ruminantFemale.NumberOfBirths = 0; } Individuals.Add(ruminantBase as Ruminant); } } return(Individuals); }
/// <summary>Checks for soil for errors.</summary> private void CheckForErrors() { const double specific_bd = 2.65; double min_sw = 0.0; for (int i = 0; i < properties.Water.Thickness.Length; i++) { double max_sw = 1.0 - MathUtilities.Divide(properties.Water.BD[i], specific_bd, 0.0); // ie. Total Porosity if (MathUtilities.IsLessThan(properties.Water.AirDry[i], min_sw)) { summary.WriteWarning(this, String.Format("({0} {1:G}) {2} {3} {4} {5} {6:G})", " Air dry lower limit of ", properties.Water.AirDry[i], " in layer ", i, "\n", " is below acceptable value of ", min_sw)); } if (MathUtilities.IsLessThan(properties.Water.LL15[i], properties.Water.AirDry[i])) { summary.WriteWarning(this, String.Format("({0} {1:G}) {2} {3} {4} {5} {6:G})", " 15 bar lower limit of ", properties.Water.LL15[i], " in layer ", i, "\n", " is below air dry value of ", properties.Water.AirDry[i])); } if (MathUtilities.IsLessThanOrEqual(properties.Water.DUL[i], properties.Water.LL15[i])) { summary.WriteWarning(this, String.Format("({0} {1:G}) {2} {3} {4} {5} {6:G})", " drained upper limit of ", properties.Water.DUL[i], " in layer ", i, "\n", " is at or below lower limit of ", properties.Water.LL15[i])); } if (MathUtilities.IsLessThanOrEqual(properties.Water.SAT[i], properties.Water.DUL[i])) { summary.WriteWarning(this, String.Format("({0} {1:G}) {2} {3} {4} {5} {6:G})", " saturation of ", properties.Water.SAT[i], " in layer ", i, "\n", " is at or below drained upper limit of ", properties.Water.DUL[i])); } if (MathUtilities.IsGreaterThan(properties.Water.SAT[i], max_sw)) { summary.WriteWarning(this, String.Format("({0} {1:G}) {2} {3} {4} {5} {6:G} {7} {8} {9:G} {10} {11} {12:G})", " saturation of ", properties.Water.SAT[i], " in layer ", i, "\n", " is above acceptable value of ", max_sw, "\n", "You must adjust bulk density (bd) to below ", (1.0 - properties.Water.SAT[i]) * specific_bd, "\n", "OR saturation (sat) to below ", max_sw)); } if (MathUtilities.IsGreaterThan(Water[i], properties.Water.SAT[i])) { summary.WriteWarning(this, String.Format("({0} {1:G}) {2} {3} {4} {5} {6:G}", " soil water of ", Water[i], " in layer ", i, "\n", " is above saturation of ", properties.Water.SAT[i])); } if (MathUtilities.IsLessThan(Water[i], properties.Water.AirDry[i])) { summary.WriteWarning(this, String.Format("({0} {1:G}) {2} {3} {4} {5} {6:G}", " soil water of ", Water[i], " in layer ", i, "\n", " is below air-dry value of ", properties.Water.AirDry[i])); } } }
/// <summary>Partitions the dm.</summary> /// <param name="Organs">The organs.</param> /// <exception cref="System.Exception">Unknown Partition Rule + PartitionRules[i]</exception> internal void PartitionDM(List <Organ1> Organs) { // Get all DM supply terms. DMSupply = 0; foreach (Organ1 Organ in Organs) { DMSupply += Organ.DMSupply; } // Tell each organ to do it's DM Demand foreach (Organ1 Organ in Organs) { Organ.DoDMDemand(DMSupply); } foreach (Organ1 Organ in Organs) { Organ.Growth.Clear(); } double dm_remaining = DMSupply; double dlt_dm_green_tot = 0.0; for (int i = 0; i != PartitionOrgans.Length; i++) { Organ1 Organ = FindOrgan(PartitionOrgans[i], Organs); if (PartitionRules[i] == "magic") { Organ.GiveDmGreen(RatioRootShoot.Value * DMSupply); } else if (PartitionRules[i] == "seasonal") // (PFR) { double uptake = RatioRootPlant * dm_remaining; Organ.GiveDmGreen(uptake); // (PFR) dm_remaining = dm_remaining - uptake; // Here total RUE is used so remaining discounts root uptake(PFR) dlt_dm_green_tot = dlt_dm_green_tot + uptake; } else { double uptake; if (PartitionRules[i] == "demand") { uptake = Math.Min(Organ.DMGreenDemand, dm_remaining); } else if (PartitionRules[i] == "frac") { uptake = Math.Min(FracDMRemainingInPart(Organ.Name) * dm_remaining, Organ.DMGreenDemand); } else if (PartitionRules[i] == "remainder") { uptake = dm_remaining; } else { throw new Exception("Unknown Partition Rule " + PartitionRules[i]); } Organ.GiveDmGreen(uptake); dm_remaining = dm_remaining - uptake; dlt_dm_green_tot = dlt_dm_green_tot + uptake; } } if (!MathUtilities.FloatsAreEqual(dlt_dm_green_tot, DMSupply, 1.0E-4f)) { string msg = "dlt_dm_green_tot mass balance is off: " + dlt_dm_green_tot.ToString("f6") + " vs " + DMSupply.ToString("f6"); Summary.WriteWarning(this, msg); } Util.Debug("Arbitrator.DMSupply=%f", DMSupply); Util.Debug("Arbitrator.dlt_dm_green_tot=%f", dlt_dm_green_tot); }
/// <summary> /// Internal method to iterate through all children in CLEM and report any parameter setting errors /// </summary> /// <param name="model"></param> /// <param name="modelPath">Pass blank string. Used for tracking model path</param> /// <returns>Boolean indicating whether validation was successful</returns> private bool Validate(IModel model, string modelPath) { string starter = "["; if (typeof(IResourceType).IsAssignableFrom(model.GetType())) { starter = "[r="; } if (model.GetType() == typeof(ResourcesHolder)) { starter = "[r="; } if (model.GetType().IsSubclassOf(typeof(ResourceBaseWithTransactions))) { starter = "[r="; } if (model.GetType() == typeof(ActivitiesHolder)) { starter = "[a="; } if (model.GetType().IsSubclassOf(typeof(CLEMActivityBase))) { starter = "[a="; } if (model.GetType().Name.Contains("Group")) { starter = "[f="; } if (model.GetType().Name.Contains("Timer")) { starter = "[f="; } if (model.GetType().Name.Contains("Filter")) { starter = "[f="; } modelPath += starter + model.Name + "]"; modelPath = modelPath.Replace("][", "]­["); bool valid = true; var validationContext = new ValidationContext(model, null, null); var validationResults = new List <ValidationResult>(); Validator.TryValidateObject(model, validationContext, validationResults, true); if (model.Name.EndsWith(" ")) { validationResults.Add(new ValidationResult("Component name cannot end with a space character", new string[] { "Name" })); } if (validationResults.Count > 0) { valid = false; // report all errors foreach (var validateError in validationResults) { // get description string text = ""; var property = model.GetType().GetProperty(validateError.MemberNames.FirstOrDefault()); if (property != null) { text = ""; if (property.GetCustomAttributes(typeof(DescriptionAttribute), true).Count() > 0) { var attribute = property.GetCustomAttributes(typeof(DescriptionAttribute), true)[0]; var description = (DescriptionAttribute)attribute; text = description.ToString(); } } string error = String.Format("@validation:Invalid parameter value in " + modelPath + "" + Environment.NewLine + "PARAMETER: " + validateError.MemberNames.FirstOrDefault()); if (text != "") { error += String.Format(Environment.NewLine + "DESCRIPTION: " + text); } error += String.Format(Environment.NewLine + "PROBLEM: " + validateError.ErrorMessage + Environment.NewLine); Summary.WriteWarning(this, error); } } foreach (var child in model.Children) { bool result = Validate(child, modelPath); if (valid && !result) { valid = false; } } return(valid); }
/// <summary> /// Issues the warning. /// </summary> /// <param name="warningText">The warning text.</param> public void IssueWarning(string warningText) { Summary.WriteWarning(thismodel, warningText); }
/// <summary>Removes a given amount of biomass (and N) from the plant.</summary> /// <param name="amountToRemove">The amount of biomass to remove (g/m2).</param> /// <param name="PreferenceForGreenOverDead">Relative preference for live over dead material during graze (>0.0).</param> /// <param name="PreferenceForLeafOverStems">Relative preference for leaf over stem-stolon material during graze (>0.0).</param> /// <param name="summary">Optional summary object.</param> public DigestibleBiomass RemoveBiomass(double amountToRemove, double PreferenceForGreenOverDead = 1.0, double PreferenceForLeafOverStems = 1.0, ISummary summary = null) { if (!MathUtilities.FloatsAreEqual(amountToRemove, 0.0)) { var allMaterial = Material.ToList(); // get existing DM and N amounts double harvestableWt = allMaterial.Sum(m => m.Consumable.Wt); double preRemovalDMShoot = harvestableWt; double preRemovalNShoot = allMaterial.Sum(m => m.Consumable.N); // Compute the fraction of each tissue to be removed var fracRemoving = new List <FractionDigestibleBiomass>(); if (MathUtilities.FloatsAreEqual(amountToRemove - harvestableWt, 0.0)) { // All existing DM is removed amountToRemove = harvestableWt; foreach (var material in allMaterial) { double frac = MathUtilities.Divide(material.Consumable.Wt, harvestableWt, 0.0); fracRemoving.Add(new FractionDigestibleBiomass(material, frac)); } } else { // Initialise the fractions to be removed (these will be normalised later) foreach (var material in allMaterial) { double frac; if (material.IsLive) { if (material.Name == "Leaf") { frac = material.Consumable.Wt * PreferenceForGreenOverDead * PreferenceForLeafOverStems; } else { frac = material.Consumable.Wt * PreferenceForGreenOverDead; } } else { if (material.Name == "Leaf") { frac = material.Consumable.Wt * PreferenceForLeafOverStems; } else { frac = material.Consumable.Wt; } } fracRemoving.Add(new FractionDigestibleBiomass(material, frac)); } // Normalise the fractions of each tissue to be removed, they should add to one double totalFrac = fracRemoving.Sum(m => m.Fraction); foreach (var f in fracRemoving) { double fracRemovable = f.Material.Consumable.Wt / amountToRemove; f.Fraction = Math.Min(fracRemovable, f.Fraction / totalFrac); } // Iterate until sum of fractions to remove is equal to one // The initial normalised fractions are based on preference and existing DM. Because the value of fracRemoving is limited // to fracRemovable, the sum of fracRemoving may not be equal to one, as it should be. We need to iterate adjusting the // values of fracRemoving until we get a sum close enough to one. The previous values are used as weighting factors for // computing new ones at each iteration. int count = 1; totalFrac = totalFrac = fracRemoving.Sum(m => m.Fraction); while (!MathUtilities.FloatsAreEqual(1.0 - totalFrac, 0.0)) { count += 1; foreach (var f in fracRemoving) { double fracRemovable = f.Material.Consumable.Wt / amountToRemove; f.Fraction = Math.Min(fracRemovable, f.Fraction / totalFrac); } totalFrac = totalFrac = fracRemoving.Sum(m => m.Fraction); if (count > 1000) { summary?.WriteWarning(forageModel as IModel, "SimpleGrazing could not remove or graze all the DM required for " + forageModel.Name); break; } } } // Get digestibility of DM being harvested (do this before updating pools) double defoliatedDigestibility = fracRemoving.Sum(m => m.Material.Digestibility * m.Fraction); // Iterate through all live material, find the associated dead material and then // tell the forage model to remove it. var liveMaterial = fracRemoving.Where(f => f.Material.IsLive).ToList(); foreach (var live in liveMaterial) { var dead = fracRemoving.Find(frac => frac.Material.Name == live.Material.Name && !frac.Material.IsLive); if (dead == null) { throw new Exception("Cannot find associated dead material while removing biomass in SimpleGrazing"); } RemoveBiomass(live.Material.Name, new OrganBiomassRemovalType() { FractionLiveToRemove = Math.Max(0.0, MathUtilities.Divide(amountToRemove * live.Fraction, live.Material.Total.Wt, 0.0)), FractionDeadToRemove = Math.Max(0.0, MathUtilities.Divide(amountToRemove * dead.Fraction, dead.Material.Total.Wt, 0.0)) }); } if (liveMaterial.Count == 0) { var deadMaterial = fracRemoving.Where(f => !f.Material.IsLive).ToList(); foreach (var dead in deadMaterial) { // This can happen for surface organic matter which only has dead material. RemoveBiomass(dead.Material.Name, new OrganBiomassRemovalType() { FractionLiveToRemove = 0, FractionDeadToRemove = Math.Max(0.0, MathUtilities.Divide(amountToRemove * dead.Fraction, dead.Material.Consumable.Wt, 0.0)) }); } } // Set outputs and check balance var defoliatedDM = preRemovalDMShoot - Material.Sum(m => m.Consumable.Wt); var defoliatedN = preRemovalNShoot - Material.Sum(m => m.Consumable.N); if (!MathUtilities.FloatsAreEqual(defoliatedDM, amountToRemove)) { throw new Exception("Removal of DM resulted in loss of mass balance"); } else { summary?.WriteMessage(forageModel as IModel, "Biomass removed from " + forageModel.Name + " by grazing: " + (defoliatedDM * 10).ToString("#0.0") + "kg/ha"); } return(new DigestibleBiomass(new DamageableBiomass(Name, new Biomass() { StructuralWt = defoliatedDM, StructuralN = defoliatedN, }, isLive: true), defoliatedDigestibility)); } return(null); }
/// <summary> /// Try to take the Resources based on Resource Request List provided. /// Returns true if it was able to take the resources it needed. /// Returns false if it was unable to take the resources it needed. /// </summary> /// <param name="ResourceRequestList"></param> /// <param name="TriggerActivityPerformed"></param> public bool TakeResources(List <ResourceRequest> ResourceRequestList, bool TriggerActivityPerformed) { bool resourceAvailable = false; // no resources required or this is an Activity folder. if ((ResourceRequestList == null) || (ResourceRequestList.Count() == 0)) { return(false); } Guid uniqueRequestID = Guid.NewGuid(); // check resource amounts available foreach (ResourceRequest request in ResourceRequestList) { request.ActivityID = uniqueRequestID; request.Available = 0; // get resource if (request.Resource == null) { //If it hasn't been assigned try and find it now. request.Resource = Resources.GetResourceItem(request, OnMissingResourceActionTypes.Ignore, OnMissingResourceActionTypes.Ignore) as IResourceType; } if (request.Resource != null) { // get amount available request.Available = Math.Min(request.Resource.Amount, request.Required); } else { if (!resourceAvailable) { // if resource does not exist in simulation assume unlimited resource available // otherwise 0 will be assigned to available when no resouces match request request.Available = request.Required; } } } // are all resources available List <ResourceRequest> shortfallRequests = ResourceRequestList.Where(a => a.Required > a.Available).ToList(); int countShortfallRequests = shortfallRequests.Count(); if (countShortfallRequests > 0) { // check what transmutations can occur Resources.TransmutateShortfall(shortfallRequests, true); } // check if need to do transmutations int countTransmutationsSuccessful = shortfallRequests.Where(a => a.TransmutationPossible == true & a.AllowTransmutation).Count(); bool allTransmutationsSuccessful = (shortfallRequests.Where(a => a.TransmutationPossible == false & a.AllowTransmutation).Count() == 0); // OR at least one transmutation successful and PerformWithPartialResources if (((countShortfallRequests > 0) & (countShortfallRequests == countTransmutationsSuccessful)) || (countTransmutationsSuccessful > 0 & OnPartialResourcesAvailableAction == OnPartialResourcesAvailableActionTypes.UseResourcesAvailable)) { // do transmutations. Resources.TransmutateShortfall(shortfallRequests, false); // recheck resource amounts now that resources have been topped up foreach (ResourceRequest request in ResourceRequestList) { // get resource request.Available = 0; if (request.Resource != null) { // get amount available request.Available = Math.Min(request.Resource.Amount, request.Required); } } } // report any resource defecits here foreach (var item in ResourceRequestList.Where(a => a.Required > a.Available)) { ResourceRequestEventArgs rrEventArgs = new ResourceRequestEventArgs() { Request = item }; OnShortfallOccurred(rrEventArgs); Status = ActivityStatus.Partial; } // remove activity resources // check if deficit and performWithPartial if ((ResourceRequestList.Where(a => a.Required > a.Available).Count() == 0) || OnPartialResourcesAvailableAction != OnPartialResourcesAvailableActionTypes.SkipActivity) { if (OnPartialResourcesAvailableAction == OnPartialResourcesAvailableActionTypes.ReportErrorAndStop) { string resourcelist = ""; foreach (var item in ResourceRequestList.Where(a => a.Required > a.Available)) { Summary.WriteWarning(this, String.Format("Insufficient ({0}) resource of type ({1}) for activity ({2})", item.ResourceType, item.ResourceTypeName, this.Name)); resourcelist += ((resourcelist.Length > 0)?",":"") + item.ResourceType.Name; } if (resourcelist.Length > 0) { Summary.WriteWarning(this, String.Format("Ensure resources are available or change OnPartialResourcesAvailableAction setting for activity ({0}) to handle previous error", this.Name)); Status = ActivityStatus.Critical; throw new Exception(String.Format("Insufficient resources ({0}) for activity ({1}) (see Summary for details)", resourcelist, this.Name)); } } foreach (ResourceRequest request in ResourceRequestList) { // get resource request.Provided = 0; if (request.Resource != null) { // remove resource request.Resource.Remove(request); } } //return true; //could take all the resources it needed or is able to do with partial amounts } else { Status = ActivityStatus.Ignored; //return false; //could not take all the resources it needed. } return(Status != ActivityStatus.Ignored); }
private void OnStartOfSimulation(object sender, EventArgs e) { // This needs to happen after all herd creation has been performed // Therefore we use StartOfSimulation event RuminantHerd ruminantHerd = Resources.RuminantHerd(); List <Ruminant> herd = ruminantHerd.Herd.Where(a => a.HerdName == HerdName).ToList(); // get list of females of breeding age and condition List <RuminantFemale> breedFemales = herd.Where(a => a.Gender == Sex.Female & a.Age >= a.BreedParams.MinimumAge1stMating + 12 & a.Weight >= (a.BreedParams.MinimumSize1stMating * a.StandardReferenceWeight) & a.Weight >= (a.BreedParams.CriticalCowWeight * a.StandardReferenceWeight)).OrderByDescending(a => a.Age).ToList().Cast <RuminantFemale>().ToList(); // get list of all sucking individuals List <Ruminant> sucklingList = herd.Where(a => a.Weaned == false).ToList(); if (breedFemales.Count() == 0) { if (sucklingList.Count > 0) { Summary.WriteWarning(this, String.Format("No breeding females to assign sucklings for herd ({0})", HerdName)); } return; } // gestation interval at smallest size generalised curve double minAnimalWeight = breedFemales[0].StandardReferenceWeight - ((1 - breedFemales[0].BreedParams.SRWBirth) * breedFemales[0].StandardReferenceWeight) * Math.Exp(-(breedFemales[0].BreedParams.AgeGrowthRateCoefficient * (breedFemales[0].BreedParams.MinimumAge1stMating * 30.4)) / (Math.Pow(breedFemales[0].StandardReferenceWeight, breedFemales[0].BreedParams.SRWGrowthScalar))); double IPIminsize = Math.Pow(breedFemales[0].BreedParams.InterParturitionIntervalIntercept * (minAnimalWeight / breedFemales[0].StandardReferenceWeight), breedFemales[0].BreedParams.InterParturitionIntervalCoefficient) * 30.64; // restrict minimum period between births IPIminsize = Math.Max(IPIminsize, breedFemales[0].BreedParams.GestationLength + 61); // assign calves to cows foreach (var suckling in sucklingList) { if (breedFemales.Count > 0) { breedFemales[0].DryBreeder = false; //Initialise female milk production in at birth so ready for sucklings to consume double milkTime = 15; // equivalent to mid month production double milkProduction = breedFemales[0].BreedParams.MilkPeakYield * breedFemales[0].Weight / breedFemales[0].NormalisedAnimalWeight * (Math.Pow(((milkTime + breedFemales[0].BreedParams.MilkOffsetDay) / breedFemales[0].BreedParams.MilkPeakDay), breedFemales[0].BreedParams.MilkCurveSuckling)) * Math.Exp(breedFemales[0].BreedParams.MilkCurveSuckling * (1 - (milkTime + breedFemales[0].BreedParams.MilkOffsetDay) / breedFemales[0].BreedParams.MilkPeakDay)); breedFemales[0].MilkProduction = Math.Max(milkProduction, 0.0); breedFemales[0].MilkAmount = milkProduction * 30.4; // generalised curve double IPIcurrent = Math.Pow(breedFemales[0].BreedParams.InterParturitionIntervalIntercept * (breedFemales[0].Weight / breedFemales[0].StandardReferenceWeight), breedFemales[0].BreedParams.InterParturitionIntervalCoefficient) * 30.64; // restrict minimum period between births IPIcurrent = Math.Max(IPIcurrent, breedFemales[0].BreedParams.GestationLength + 61); breedFemales[0].NumberOfBirths = Convert.ToInt32((breedFemales[0].Age - suckling.Age - breedFemales[0].BreedParams.GestationLength - breedFemales[0].BreedParams.MinimumAge1stMating) / ((IPIcurrent + IPIminsize) / 2)); //breedFemales[0].Parity = breedFemales[0].Age - suckling.Age - 9; // I removed the -9 as this would make it conception month not birth month breedFemales[0].AgeAtLastBirth = breedFemales[0].Age - suckling.Age; breedFemales[0].AgeAtLastConception = breedFemales[0].AgeAtLastBirth - breedFemales[0].BreedParams.GestationLength; // suckling mother set suckling.Mother = breedFemales[0]; // check if a twin and if so apply next individual to same mother. // otherwise remove this mother from the list if (randomGenerator.RandNo >= breedFemales[0].BreedParams.TwinRate) { breedFemales.RemoveAt(0); } } else { Summary.WriteWarning(this, String.Format("Insufficient breeding females to assign sucklings for herd ({0})", HerdName)); return; } } // assing values for the remaining females who haven't just bred. foreach (var female in breedFemales) { female.DryBreeder = true; // generalised curve double IPIcurrent = Math.Pow(breedFemales[0].BreedParams.InterParturitionIntervalIntercept * (breedFemales[0].Weight / breedFemales[0].StandardReferenceWeight), breedFemales[0].BreedParams.InterParturitionIntervalCoefficient) * 30.64; // restrict minimum period between births IPIcurrent = Math.Max(IPIcurrent, breedFemales[0].BreedParams.GestationLength + 61); breedFemales[0].NumberOfBirths = Convert.ToInt32((breedFemales[0].Age - breedFemales[0].BreedParams.MinimumAge1stMating) / ((IPIcurrent + IPIminsize) / 2)) - 1; female.AgeAtLastBirth = breedFemales[0].Age - 12; } }
private void OnWFFeedAllocation(object sender, EventArgs e) { // if no requests end if (requests.Count() == 0) { return; } // month needed for montly pasture limits. int month = Clock.Today.Month; // get list of unique priorities provided in requests List <int> priorityList = requests.OrderBy(b => b.FeedActivity.FeedPriority).Select(a => a.FeedActivity.FeedPriority).Distinct().ToList(); foreach (int priority in priorityList) { // group by feed types requested with each priority var feedGrouping = requests.Where(a => a.FeedActivity.FeedPriority == priority).GroupBy(a => a.FeedActivity.FeedType); foreach (var feedTypeGroup in feedGrouping) { IFeedType feedType = feedTypeGroup.Key as IFeedType; // determine the total requested for each feedtype double amountRequested = feedTypeGroup.Sum(a => Math.Min(a.Amount, (a.Requestor.PotentialIntake - a.Requestor.Intake) * 30.4) * a.Requestor.Number); // if something requested and something available if (amountRequested > 0 & feedType.Amount > 0) { // determine if shortfall double deficit = Math.Min(1.0, feedType.Amount / amountRequested); if (deficit < 1.0) { // TODO: work out what do do. // Buy fodder // Use common land // use Animal Food Stores. } // prepare to take available from resource store amountRequested *= deficit; // group requests by ruminant breed to allow calculations of limits var breedGrouping = feedTypeGroup.GroupBy(a => a.Requestor.BreedParams.Name); foreach (var breedGroup in breedGrouping) { // If feeding on pasture calculate limits based on proportion green // This needs to be done for pasture/breed combinations each month // So cannot be done for Ruminants or Pasture seperately at start of month if (feedType.GetType().Name == "PastureType") { GrazeFoodStoreType pasture = feedType as GrazeFoodStoreType; double total = 0; foreach (var pool in pasture.Pools) { pool.Limit = 1.0; total += pool.Amount; } // if Jan-March then user first three months otherwise use 2 int greenage = 2; if (month <= 3) { greenage = 3; } double green = pasture.Pools.Where(a => (a.Age <= greenage)).Sum(b => b.Amount); double propgreen = green / total; double greenlimit = breedGroup.FirstOrDefault().Requestor.BreedParams.GreenDietMax *(1 - Math.Exp(-breedGroup.FirstOrDefault().Requestor.BreedParams.GreenDietCoefficient *((propgreen * 100.0) - breedGroup.FirstOrDefault().Requestor.BreedParams.GreenDietZero))); greenlimit = Math.Max(0.0, greenlimit); if (propgreen > 90) { greenlimit = 100; } foreach (var pool in pasture.Pools.Where(a => a.Age <= greenage)) { pool.Limit = greenlimit / 100.0; } } // update Ruminants Intake, ProteinConcentration and DietDryMatterDigestibility foreach (var request in feedTypeGroup) { switch (request.FeedActivity.FeedType.GetType().Name) { case "GrazeFoodStoreType": // take from pools as specified for the individual GrazeFoodStoreType pasture = request.FeedActivity.FeedType as GrazeFoodStoreType; double amountRequired = (request.Requestor.PotentialIntake - request.Requestor.Intake) * 30.4; int index = 0; bool secondTakeFromPools = request.Requestor.BreedParams.StrictFeedingLimits; while (amountRequired > 0) { // limiter obtained from filter group or unlimited if second take of pools double limiter = 1.0; if (!secondTakeFromPools) { limiter = pasture.Pools[index].Limit; } double amountToRemove = Math.Min(pasture.Pools[index].Amount, amountRequired * limiter); amountRequired -= amountToRemove; request.Amount = amountToRemove; pasture.Pools[index].Remove(request); index++; if (index >= pasture.Pools.Count) { // if we've already given second chance to get food so finish without full satisfying individual // or strict feeding limits are enforced if (secondTakeFromPools) { break; } // if not strict limits allow a second request for food from previously limited pools. secondTakeFromPools = true; index = 0; } } break; case "AnimalFoodStoreType": // take directly from store if available request.Amount *= deficit; feedTypeGroup.FirstOrDefault().FeedActivity.FeedType.Remove(request); break; default: string error = String.Format("Unrecognised feed type {0} in {1} of name {2}", request.GetType().ToString(), this.GetType().ToString(), this.Name); Summary.WriteWarning(this, error); throw new Exception("Unrecognised Feedtype found in feed request"); } } } } } } }
private void OnCLEMInitialiseResource(object sender, EventArgs e) { id = 1; Herd = new List <Ruminant>(); PurchaseIndividuals = new List <Ruminant>(); LastIndividualChanged = new Ruminant(); // for each Ruminant type foreach (RuminantType rType in Apsim.Children(this, typeof(RuminantType))) { foreach (RuminantInitialCohorts ruminantCohorts in Apsim.Children(rType, typeof(RuminantInitialCohorts))) { foreach (var ind in ruminantCohorts.CreateIndividuals()) { ind.SaleFlag = HerdChangeReason.InitialHerd; AddRuminant(ind); } } } // Assign mothers to suckling calves foreach (string HerdName in Herd.Select(a => a.HerdName).Distinct()) { List <Ruminant> herd = Herd.Where(a => a.HerdName == HerdName).ToList(); // get list of females of breeding age and condition List <RuminantFemale> breedFemales = herd.Where(a => a.Gender == Sex.Female & a.Age >= a.BreedParams.MinimumAge1stMating + a.BreedParams.GestationLength & a.Weight >= (a.BreedParams.MinimumSize1stMating * a.StandardReferenceWeight) & a.Weight >= (a.BreedParams.CriticalCowWeight * a.StandardReferenceWeight)).OrderByDescending(a => a.Age).ToList().Cast <RuminantFemale>().ToList(); // get list of all sucking individuals List <Ruminant> sucklingList = herd.Where(a => a.Weaned == false).ToList(); if (breedFemales.Count() == 0) { if (sucklingList.Count > 0) { Summary.WriteWarning(this, String.Format("Insufficient breeding females to assign ({0}) sucklings for herd ({1})", sucklingList.Count, HerdName)); } } else { // gestation interval at smallest size generalised curve double minAnimalWeight = breedFemales[0].StandardReferenceWeight - ((1 - breedFemales[0].BreedParams.SRWBirth) * breedFemales[0].StandardReferenceWeight) * Math.Exp(-(breedFemales[0].BreedParams.AgeGrowthRateCoefficient * (breedFemales[0].BreedParams.MinimumAge1stMating * 30.4)) / (Math.Pow(breedFemales[0].StandardReferenceWeight, breedFemales[0].BreedParams.SRWGrowthScalar))); double IPIminsize = Math.Pow(breedFemales[0].BreedParams.InterParturitionIntervalIntercept * (minAnimalWeight / breedFemales[0].StandardReferenceWeight), breedFemales[0].BreedParams.InterParturitionIntervalCoefficient) * 30.64; // restrict minimum period between births IPIminsize = Math.Max(IPIminsize, breedFemales[0].BreedParams.GestationLength + 61); // assign calves to cows int sucklingCount = 0; foreach (var suckling in sucklingList) { sucklingCount++; if (breedFemales.Count > 0) { breedFemales[0].DryBreeder = false; //Initialise female milk production in at birth so ready for sucklings to consume double milkTime = 15; // equivalent to mid month production // need to calculate normalised animal weight here for milk production breedFemales[0].NormalisedAnimalWeight = breedFemales[0].StandardReferenceWeight - ((1 - breedFemales[0].BreedParams.SRWBirth) * breedFemales[0].StandardReferenceWeight) * Math.Exp(-(breedFemales[0].BreedParams.AgeGrowthRateCoefficient * (breedFemales[0].Age * 30.4)) / (Math.Pow(breedFemales[0].StandardReferenceWeight, breedFemales[0].BreedParams.SRWGrowthScalar))); double milkProduction = breedFemales[0].BreedParams.MilkPeakYield * breedFemales[0].Weight / breedFemales[0].NormalisedAnimalWeight * (Math.Pow(((milkTime + breedFemales[0].BreedParams.MilkOffsetDay) / breedFemales[0].BreedParams.MilkPeakDay), breedFemales[0].BreedParams.MilkCurveSuckling)) * Math.Exp(breedFemales[0].BreedParams.MilkCurveSuckling * (1 - (milkTime + breedFemales[0].BreedParams.MilkOffsetDay) / breedFemales[0].BreedParams.MilkPeakDay)); breedFemales[0].MilkProduction = Math.Max(milkProduction, 0.0); breedFemales[0].MilkAmount = milkProduction * 30.4; // generalised curve double IPIcurrent = Math.Pow(breedFemales[0].BreedParams.InterParturitionIntervalIntercept * (breedFemales[0].Weight / breedFemales[0].StandardReferenceWeight), breedFemales[0].BreedParams.InterParturitionIntervalCoefficient) * 30.64; // restrict minimum period between births IPIcurrent = Math.Max(IPIcurrent, breedFemales[0].BreedParams.GestationLength + 61); breedFemales[0].NumberOfBirths = Convert.ToInt32((breedFemales[0].Age - suckling.Age - breedFemales[0].BreedParams.GestationLength - breedFemales[0].BreedParams.MinimumAge1stMating) / ((IPIcurrent + IPIminsize) / 2)); //breedFemales[0].Parity = breedFemales[0].Age - suckling.Age - 9; // I removed the -9 as this would make it conception month not birth month breedFemales[0].AgeAtLastBirth = breedFemales[0].Age - suckling.Age; breedFemales[0].AgeAtLastConception = breedFemales[0].AgeAtLastBirth - breedFemales[0].BreedParams.GestationLength; breedFemales[0].SuccessfulPregnancy = true; // suckling mother set suckling.Mother = breedFemales[0]; // add suckling to suckling offspring of mother. suckling.Mother.SucklingOffspring.Add(suckling); // check if a twin and if so apply next individual to same mother. // otherwise remove this mother from the list if (ZoneCLEM.RandomGenerator.NextDouble() >= breedFemales[0].BreedParams.TwinRate) { breedFemales.RemoveAt(0); } } else { Summary.WriteWarning(this, String.Format("Insufficient breeding females to assign ({0}) sucklings for herd ({1})", sucklingList.Count - sucklingCount, HerdName)); } } // assigning values for the remaining females who haven't just bred. foreach (var female in breedFemales) { female.DryBreeder = true; // generalised curve double IPIcurrent = Math.Pow(breedFemales[0].BreedParams.InterParturitionIntervalIntercept * (breedFemales[0].Weight / breedFemales[0].StandardReferenceWeight), breedFemales[0].BreedParams.InterParturitionIntervalCoefficient) * 30.64; // restrict minimum period between births IPIcurrent = Math.Max(IPIcurrent, breedFemales[0].BreedParams.GestationLength + 61); breedFemales[0].NumberOfBirths = Convert.ToInt32((breedFemales[0].Age - breedFemales[0].BreedParams.MinimumAge1stMating) / ((IPIcurrent + IPIminsize) / 2)) - 1; female.AgeAtLastBirth = breedFemales[0].Age - 12; } } } //List<IModel> childNodes = Apsim.Children(this, typeof(IModel)); //foreach (IModel childModel in childNodes) //{ // //cast the generic IModel to a specfic model. // RuminantType ruminantType = childModel as RuminantType; // foreach (var ind in ruminantType.CreateIndividuals()) // { // ind.SaleFlag = HerdChangeReason.InitialHerd; // AddRuminant(ind); // } //} }
private void OnCommencing(object sender, EventArgs e) { int lossModifier = 1; if (ReportLossesAsNegative) { lossModifier = -1; } // check if running from a CLEM.Market bool market = (FindAncestor <Zone>().GetType() == typeof(Market)); dataToWriteToDb = null; // sanitise the variable names and remove duplicates List <string> variableNames = new List <string> { "[Clock].Today as Date" }; if (VariableNames != null && VariableNames.Count() > 0) { if (VariableNames.Count() > 1) { Summary.WriteWarning(this, String.Format("Multiple resource groups not permitted in ReportResourceLedger [{0}]\r\nAdditional entries have been ignored", this.Name)); } for (int i = 0; i < 1; i++) { // each variable name is now a ResourceGroup bool isDuplicate = StringUtilities.IndexOfCaseInsensitive(variableNames, this.VariableNames[i].Trim()) != -1; if (!isDuplicate && this.VariableNames[i] != string.Empty) { // check it is a ResourceGroup CLEMModel model = Resources.GetResourceGroupByName(this.VariableNames[i]) as CLEMModel; if (model == null) { Summary.WriteWarning(this, String.Format("Invalid resource group [{0}] in ReportResourceBalances [{1}]\r\nEntry has been ignored", this.VariableNames[i], this.Name)); } else { bool pricingIncluded = false; if (model.GetType() == typeof(RuminantHerd)) { pricingIncluded = model.FindAllDescendants <AnimalPricing>().Where(a => a.Enabled).Count() > 0; variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ExtraInformation.ID as uID"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ExtraInformation.Breed as Breed"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ExtraInformation.Gender as Sex"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ExtraInformation.Age as Age"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ExtraInformation.Weight as Weight"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ExtraInformation.AdultEquivalent as AE"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ExtraInformation.SaleFlag as Category"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ExtraInformation.HerdName as RelatesTo"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ExtraInformation.PopulationChangeDirection as Change"); // ToDo: add pricing for ruminants including buy and sell pricing // Needs update in CLEMResourceTypeBase and link between ResourcePricing and AnimalPricing. } else { pricingIncluded = model.FindAllDescendants <ResourcePricing>().Where(a => a.Enabled).Count() > 0; if (ReportStyle == ReportTransactionStyle.GainAndLossColumns) { variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.Gain as Gain"); variableNames.Add("[Resources]." + this.VariableNames[i] + $".LastTransaction.Loss * {lossModifier} as Loss"); } else { variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.TransactionType as Type"); variableNames.Add("[Resources]." + this.VariableNames[i] + $".LastTransaction.AmountModifiedForLoss({ReportLossesAsNegative}) as Amount"); } // get all converters for this type of resource if (IncludeConversions) { var converterList = model.FindAllDescendants <ResourceUnitsConverter>().Select(a => a.Name).Distinct(); if (converterList != null) { foreach (var item in converterList) { if (ReportStyle == ReportTransactionStyle.GainAndLossColumns) { variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ConvertTo(" + item + $",\"gain\",{ReportLossesAsNegative}) as " + item + "_Gain"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ConvertTo(" + item + $",\"loss\",{ReportLossesAsNegative}) as " + item + "_Loss"); } else { variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ConvertTo(" + item + $"\", {ReportLossesAsNegative}) as " + item + "_Amount"); } } } } // add pricing if (IncludePrice && pricingIncluded) { if (ReportStyle == ReportTransactionStyle.GainAndLossColumns) { variableNames.Add("[Resources]." + this.VariableNames[i] + $".LastTransaction.ConvertTo(\"$gain\",\"gain\", {ReportLossesAsNegative}) as Price_Gain"); variableNames.Add("[Resources]." + this.VariableNames[i] + $".LastTransaction.ConvertTo(\"$loss\",\"loss\", {ReportLossesAsNegative}) as Price_Loss"); } else { variableNames.Add("[Resources]." + this.VariableNames[i] + $".LastTransaction.ConvertTo(\"$gain\", {ReportLossesAsNegative}) as Price_Amount"); } } variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ResourceType.Name as Resource"); // if this is a multi CLEM model simulation then add a new column with the parent Zone name if (FindAncestor <Simulation>().FindChild <Market>() != null) { variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.Activity.CLEMParentName as Source"); } variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.Activity.Name as Activity"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.RelatesToResource as RelatesTo"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.Category as Category"); } } } } events.Subscribe("[Resources]." + this.VariableNames[0] + ".TransactionOccurred", DoOutputEvent); } // Tidy up variable/event names. base.VariableNames = variableNames.ToArray(); VariableNames = variableNames.ToArray(); base.VariableNames = TidyUpVariableNames(); VariableNames = base.VariableNames; base.EventNames = TidyUpEventNames(); EventNames = base.EventNames; this.FindVariableMembers(); }
private void OnCommencing(object sender, EventArgs e) { int lossModifier = 1; if (ReportLossesAsNegative) { lossModifier = -1; } // check if running from a CLEM.Market bool market = (FindAncestor <Zone>().GetType() == typeof(Market)); List <string> variableNames = new List <string> { "[Clock].Today as Date" }; List <string> eventNames = new List <string>(); if (ResourceGroupsToReport != null && ResourceGroupsToReport.Trim() != "") { // check it is a ResourceGroup CLEMModel model = resources.FindResource <ResourceBaseWithTransactions>(ResourceGroupsToReport); if (model == null) { summary.WriteWarning(this, String.Format("Invalid resource group [{0}] in ReportResourceBalances [{1}]\r\nEntry has been ignored", this.ResourceGroupsToReport, this.Name)); } else { bool pricingIncluded = false; if (model.GetType() == typeof(RuminantHerd)) { pricingIncluded = model.FindAllDescendants <AnimalPricing>().Where(a => a.Enabled).Count() > 0; variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastIndividualChanged.ID as uID"); variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastIndividualChanged.Breed as Breed"); variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastIndividualChanged.Sex as Sex"); variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastIndividualChanged.Age as Age"); variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastIndividualChanged.Weight as Weight"); variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastIndividualChanged.AdultEquivalent as AE"); variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastIndividualChanged.SaleFlag as Category"); variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastIndividualChanged.Class as Class"); variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastIndividualChanged.HerdName as RelatesTo"); variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastIndividualChanged.PopulationChangeDirection as Change"); // ToDo: add pricing for ruminants including buy and sell pricing // Needs update in CLEMResourceTypeBase and link between ResourcePricing and AnimalPricing. } else { pricingIncluded = model.FindAllDescendants <ResourcePricing>().Where(a => a.Enabled).Count() > 0; if (ReportStyle == ReportTransactionStyle.GainAndLossColumns) { variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastTransaction.Gain as Gain"); variableNames.Add("[Resources]." + this.ResourceGroupsToReport + $".LastTransaction.Loss * {lossModifier} as Loss"); } else { variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastTransaction.TransactionType as Type"); variableNames.Add("[Resources]." + this.ResourceGroupsToReport + $".LastTransaction.AmountModifiedForLoss({ReportLossesAsNegative}) as Amount"); } // get all converters for this type of resource if (IncludeConversions) { var converterList = model.FindAllDescendants <ResourceUnitsConverter>().Select(a => a.Name).Distinct(); if (converterList != null) { foreach (var item in converterList) { if (ReportStyle == ReportTransactionStyle.GainAndLossColumns) { variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastTransaction.ConvertTo(" + item + $",\"gain\",{ReportLossesAsNegative}) as " + item + "_Gain"); variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastTransaction.ConvertTo(" + item + $",\"loss\",{ReportLossesAsNegative}) as " + item + "_Loss"); } else { variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastTransaction.ConvertTo(" + item + $"\", {ReportLossesAsNegative}) as " + item + "_Amount"); } } } } // add pricing if (IncludePrice && pricingIncluded) { if (ReportStyle == ReportTransactionStyle.GainAndLossColumns) { variableNames.Add("[Resources]." + this.ResourceGroupsToReport + $".LastTransaction.ConvertTo(\"$gain\",\"gain\", {ReportLossesAsNegative}) as Price_Gain"); variableNames.Add("[Resources]." + this.ResourceGroupsToReport + $".LastTransaction.ConvertTo(\"$loss\",\"loss\", {ReportLossesAsNegative}) as Price_Loss"); } else { variableNames.Add("[Resources]." + this.ResourceGroupsToReport + $".LastTransaction.ConvertTo(\"$gain\", {ReportLossesAsNegative}) as Price_Amount"); } } variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastTransaction.ResourceType.Name as Resource"); // if this is a multi CLEM model simulation then add a new column with the parent Zone name if (FindAncestor <Simulation>().FindChild <Market>() != null) { variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastTransaction.Activity.CLEMParentName as Source"); } variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastTransaction.Activity.Name as Activity"); variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastTransaction.RelatesToResource as RelatesTo"); variableNames.Add("[Resources]." + this.ResourceGroupsToReport + ".LastTransaction.Category as Category"); } } eventNames.Add("[Resources]." + this.ResourceGroupsToReport + ".TransactionOccurred"); } VariableNames = variableNames.ToArray(); EventNames = eventNames.ToArray(); SubscribeToEvents(); }
private void OnWFAnimalSell(object sender, EventArgs e) { RuminantHerd ruminantHerd = Resources.RuminantHerd(); Finance Accounts = Resources.FinanceResource() as Finance; FinanceType bankAccount = Accounts.GetFirst(); int trucks = 0; double saleValue = 0; double saleWeight = 0; int head = 0; // get current untrucked list of animals flagged for sale List <Ruminant> herd = ruminantHerd.Herd.Where(a => a.SaleFlag != Common.HerdChangeReason.None & a.Breed == BreedName).OrderByDescending(a => a.Weight).ToList(); // if sale herd > min loads before allowing sale if (herd.Select(a => a.Weight / 450.0).Sum() / Number450kgPerTruck >= MinimumTrucksBeforeSelling) { // while truck to fill while (herd.Select(a => a.Weight / 450.0).Sum() / Number450kgPerTruck > MinimumLoadBeforeSelling) { bool nonloaded = true; trucks++; double load450kgs = 0; // while truck below carrying capacity load individuals foreach (var ind in herd) { if (load450kgs + (ind.Weight / 450.0) <= Number450kgPerTruck) { nonloaded = false; head++; load450kgs += ind.Weight / 450.0; RuminantValue getvalue = PriceList.Where(a => a.Age < ind.Age).OrderBy(a => a.Age).LastOrDefault(); saleValue += getvalue.SellValue * ((getvalue.Style == Common.PricingStyleType.perKg) ? ind.Weight : 1.0); saleWeight += ind.Weight; ruminantHerd.RemoveRuminant(ind); } } if (nonloaded) { Summary.WriteWarning(this, String.Format("There was a problem loading the sale truck as sale individuals did not meet the loading criteria for breed {0}", BreedName)); break; } herd = ruminantHerd.Herd.Where(a => a.SaleFlag != Common.HerdChangeReason.None & a.Breed == BreedName).OrderByDescending(a => a.Weight).ToList(); } if (trucks > 0 & bankAccount != null) { // calculate transport costs double transportCost = trucks * DistanceToMarket * CostPerKmTrucking; bankAccount.Remove(transportCost, this.Name, "Transport"); // calculate MLA fees double mlaCost = head * MLAFees; bankAccount.Remove(mlaCost, this.Name, "R&DFee"); // calculate yard fees double yardCost = head * YardFees; bankAccount.Remove(yardCost, this.Name, "YardCosts"); // calculate commission double commissionCost = saleValue * SalesCommission; bankAccount.Remove(commissionCost, this.Name, "SalesCommission"); // add and remove from bank bankAccount.Add(saleValue, this.Name, "Sales"); } } }
/// <summary>This is only called from a Plant1 process method - not used in Plant2.</summary> /// <returns></returns> internal bool PlantDeath() { das++; if (Phenology.InPhase("sowing")) { dlt_plants_failure_germ = CropFailureGermination(); } else { dlt_plants_failure_germ = 0.0; } if (Phenology.InPhase("germination")) { dlt_plants_failure_emergence = CropFailureEmergence(); } else { dlt_plants_failure_emergence = 0.0; } dlt_plants_death_seedling = 0.0; if (Phenology.OnDayOf("emergence")) { dlt_plants_death_seedling = DeathSeedling(); } /*XXXX this needs to be coupled with dlt_leaf_area_sen, c_sen_start_stage FIXME*/ if (!Phenology.InPhase("SowingToGermination") && !Phenology.InPhase("GerminationToEmergence")) { dlt_plants_failure_leaf_sen = CropFailureLeafSen(); } else { dlt_plants_failure_leaf_sen = 0.0; } if (CropFailureStressPeriod.Value() == 1) { dlt_plants_failure_phen_delay = CropFailurePhenDelay(); } else { dlt_plants_failure_phen_delay = 0.0; } if (CropFailureStressPeriod.Value() == 1) { dlt_plants_death_drought = DeathDrought(); } else { dlt_plants_death_drought = 0.0; } DeathActual(); Util.Debug("Population.dlt_plants=%f", dlt_plants); if (MathUtilities.FloatsAreEqual(dlt_plants + Density, 0.0)) { //!!!!! fix problem with deltas in update when change from alive to dead ?zero deltas double biomass = 0; foreach (Organ1 Organ in Plant.Tops) { biomass += (Organ.Live.Wt + Organ.Dead.Wt) * Conversions.gm2kg / Conversions.sm2ha; } // report string msg = "Plant death. standing above-ground dm = " + biomass.ToString("f2") + " (kg/ha)"; Summary.WriteWarning(this, msg); return(true); // XX Needs to signal a need to call zero_variables here... // Present method is to rely on calling zero_xx at tomorrow's prepare() event.. :( } return(false); }
/// <summary> /// Retrieve a ResourceType from a ResourceGroup based on a request item including filter and sort options /// </summary> /// <param name="Request">A resource request item</param> /// <param name="MissingResourceAction">Action to take if requested resource group not found</param> /// <param name="MissingResourceTypeAction">Action to take if requested resource type not found</param> /// <returns>A reference to the item of type Model</returns> public Model GetResourceItem(ResourceRequest Request, OnMissingResourceActionTypes MissingResourceAction, OnMissingResourceActionTypes MissingResourceTypeAction) { if (Request.FilterDetails != null) { if (Request.ResourceType == null) { string errorMsg = String.Format("Resource type must be supplied in resource request from {0}", Request.ActivityModel.Name); Summary.WriteWarning(Request.ActivityModel, String.Format("Resource type must be supplied in resource request from {0}", Request.ActivityModel.Name)); throw new Exception(errorMsg); } IModel resourceGroup = this.GetByType(Request.ResourceType); if (resourceGroup == null) { string errorMsg = String.Format("Unable to locate resources of type ({0}) for ({1})", Request.ResourceType, Request.ActivityModel.Name); switch (MissingResourceAction) { case OnMissingResourceActionTypes.ReportErrorAndStop: throw new Exception(errorMsg); case OnMissingResourceActionTypes.ReportWarning: Summary.WriteWarning(Request.ActivityModel, errorMsg); break; default: break; } return(null); } // get list of children matching the conditions in filter // and return the lowest item that has enough time available object resourceGroupObject = resourceGroup as object; switch (resourceGroupObject.GetType().ToString()) { case "Models.CLEM.Resources.Labour": // get matching labour types // use activity uid to ensure unique for this request List <LabourType> items = (resourceGroup as Labour).Items; items = items.Filter(Request.FilterDetails.FirstOrDefault() as Model); items = items.Where(a => a.LastActivityRequestID != Request.ActivityID).ToList(); if (items.Where(a => a.Amount >= Request.Required).Count() > 0) { // get labour least available but with the amount needed return(items.Where(a => a.Amount >= Request.Required).OrderByDescending(a => a.Amount).FirstOrDefault()); } else { // get labour with most available but with less than the amount needed return(items.OrderByDescending(a => a.Amount).FirstOrDefault()); } default: string errorMsg = "Resource cannot be filtered. Filtering not implemented for " + resourceGroupObject.GetType().ToString() + " from activity (" + Request.ActivityModel.Name + ")"; Summary.WriteWarning(Request.ActivityModel, errorMsg); throw new Exception(errorMsg); } } else { return(GetResourceItem(Request.ActivityModel, Request.ResourceType, Request.ResourceTypeName, MissingResourceAction, MissingResourceTypeAction)); } }
private void OnCommencing(object sender, EventArgs e) { dataToWriteToDb = null; // sanitise the variable names and remove duplicates List <string> variableNames = new List <string> { "[Clock].Today" }; if (VariableNames != null && VariableNames.Count() > 0) { if (VariableNames.Count() > 1) { Summary.WriteWarning(this, String.Format("Multiple resource groups not permitted in ReportResourceLedger [{0}]\nAdditional entries have been ignored", this.Name)); } for (int i = 0; i < 1; i++) { // each variable name is now a ResourceGroup bool isDuplicate = StringUtilities.IndexOfCaseInsensitive(variableNames, this.VariableNames[i].Trim()) != -1; if (!isDuplicate && this.VariableNames[i] != string.Empty) { // check it is a ResourceGroup CLEMModel model = Resources.GetResourceGroupByName(this.VariableNames[i]) as CLEMModel; if (model == null) { Summary.WriteWarning(this, String.Format("Invalid resource group [{0}] in ReportResourceBalances [{1}]\nEntry has been ignored", this.VariableNames[i], this.Name)); } else { bool pricingIncluded = false; if (model.GetType() == typeof(RuminantHerd)) { pricingIncluded = Apsim.ChildrenRecursively(model, typeof(AnimalPricing)).Count() > 0; variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ExtraInformation.ID as uID"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ExtraInformation.Breed as Breed"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ExtraInformation.HerdName as Herd"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ExtraInformation.GenderAsString as Sex"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ExtraInformation.Age as Age"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ExtraInformation.Weight as Weight"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ExtraInformation.SaleFlagAsString as Reason"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ExtraInformation.PopulationChangeDirection as Change"); // ToDo: add pricing for ruminants including buy and sell pricing // Needs update in CLEMResourceTypeBase and link between ResourcePricing and AnimalPricing. } else { pricingIncluded = Apsim.ChildrenRecursively(model, typeof(ResourcePricing)).Count() > 0; variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.Gain as Gain"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.Loss * -1.0 as Loss"); // get all converters for this type of resource var converterList = Apsim.ChildrenRecursively(model, typeof(ResourceUnitsConverter)).Select(a => a.Name).Distinct(); if (converterList != null) { foreach (var item in converterList) { variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ConvertTo(" + item + ",\"gain\") as " + item + "_Gain"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ConvertTo(" + item + ",\"loss\") as " + item + "_Loss"); } } // add pricing if (pricingIncluded) { variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ConvertTo(\"$\",\"gain\") as Price_Gain"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ConvertTo(\"$\",\"loss\") as Price_Loss"); } variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.ResourceType.Name as Resource"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.Activity.Name as Activity"); variableNames.Add("[Resources]." + this.VariableNames[i] + ".LastTransaction.Reason as Reason"); } } } } events.Subscribe("[Resources]." + this.VariableNames[0] + ".TransactionOccurred", DoOutputEvent); } // Tidy up variable/event names. VariableNames = variableNames.ToArray(); VariableNames = TidyUpVariableNames(); EventNames = TidyUpEventNames(); this.FindVariableMembers(); }
/// <summary> /// Handles the addition of new CNPatches /// </summary> /// <param name="PatchtoAdd">Patch data</param> private void AddNewCNPatch(AddSoilCNPatchwithFOMType PatchtoAdd) { List <int> idPatchesJustAdded = new List <int>(); // list of IDs of patches created (exclude patches that would be too small) List <int> idPatchesToDelete = new List <int>(); //list of IDs of existing patches that became too small and need to be deleted List <int> idPatchesAffected; //list of IDs of patches affected by new addition // 1. get the list of id's of patches which are affected by this addition, and the area affected double AreaAffected = 0; if (PatchtoAdd.DepositionType == DepositionTypeEnum.ToNewPatch) { // check which patches are affected idPatchesAffected = CheckPatchIDs(PatchtoAdd.AffectedPatches_id, PatchtoAdd.AffectedPatches_nm); for (int i = 0; i < idPatchesAffected.Count; i++) { AreaAffected += patches[idPatchesAffected[i]].RelativeArea; } } else if (PatchtoAdd.DepositionType == DepositionTypeEnum.NewOverlappingPatches) { // all patches are affected idPatchesAffected = new List <int>(); for (int k = 0; k < patches.Count; k++) { idPatchesAffected.Add(k); } AreaAffected = 1.0; } else { idPatchesAffected = new List <int>(); } // check that total area of affected patches is larger than new patch area if (AreaAffected - PatchtoAdd.AreaNewPatch < -minimumPatchArea) { throw new Exception(" AddSoilCNPatch - area of selected patches (" + AreaAffected.ToString("#0.00#") + ") is smaller than area of new patch(" + PatchtoAdd.AreaNewPatch.ToString("#0.00#") + "). Command cannot be executed"); } else { // check the area for each patch for (int i = 0; i < idPatchesAffected.Count; i++) { double OldPatch_OldArea = patches[idPatchesAffected[i]].RelativeArea; double NewPatch_NewArea = PatchtoAdd.AreaNewPatch * (OldPatch_OldArea / AreaAffected); double OldPatch_NewArea = OldPatch_OldArea - NewPatch_NewArea; if (NewPatch_NewArea < minimumPatchArea) { // area of patch to create is too small, patch will not be created throw new Exception(" attempt to create a new patch with area too small or negative (" + NewPatch_NewArea.ToString("#0.00#") + "). The patch will not be created. Command cannot be executed"); } else if (OldPatch_NewArea < -minimumPatchArea) { // area of patch to create is too big, patch will not be created throw new Exception(" attempt to create a new patch with area greater than the existing patch area (" + NewPatch_NewArea.ToString("#0.00#") + "). The patch will not be created. Command cannot be executed"); } else if (OldPatch_NewArea < minimumPatchArea) { // remaining area is too small or negative, patch will be created but old one will be deleted summary.WriteWarning(this, " attempt to set the area of existing patch(" + idPatchesAffected[i].ToString() + ") to a value too small or negative (" + OldPatch_NewArea.ToString("#0.00#") + "). The patch will be eliminated."); // mark old patch for deletion idPatchesToDelete.Add(idPatchesAffected[i]); // create new patch based on old one - the original one will be deleted later ClonePatch(idPatchesAffected[i]); int k = patches.Count - 1; patches[k].RelativeArea = NewPatch_NewArea; if (PatchtoAdd.AreaNewPatch > 0) { // a name was supplied patches[k].Name = PatchtoAdd.PatchName + "_" + i.ToString(); } else { // use default naming patches[k].Name = "patch" + k.ToString(); } patches[k].CreationDate = clock.Today; idPatchesJustAdded.Add(k); } else { // create new patch by spliting an existing one ClonePatch(idPatchesAffected[i]); patches[idPatchesAffected[i]].RelativeArea = OldPatch_NewArea; int k = patches.Count - 1; patches[k].RelativeArea = NewPatch_NewArea; if (PatchtoAdd.PatchName.Length > 0) { // a name was supplied patches[k].Name = PatchtoAdd.PatchName + "_" + i.ToString(); } else { // use default naming patches[k].Name = "patch" + k.ToString(); } patches[k].CreationDate = clock.Today; idPatchesJustAdded.Add(k); if (!PatchtoAdd.SuppressMessages) { summary.WriteMessage(this, "create new patch, with area = " + NewPatch_NewArea.ToString("#0.00#") + ", based on existing patch(" + idPatchesAffected[i].ToString() + ") - Old area = " + OldPatch_OldArea.ToString("#0.00#") + ", new area = " + OldPatch_NewArea.ToString("#0.00#")); } } } } // add the stuff to patches just created AddStuffToPatches(idPatchesJustAdded, PatchtoAdd); // delete the patches in excess if (idPatchesToDelete.Count > 0) { DeletePatches(idPatchesToDelete); } }