From 8c089e85098698d343ad7c965b5b22ffa48b4598 Mon Sep 17 00:00:00 2001 From: "NEXUS\\hut104" Date: Tue, 6 Nov 2018 12:30:36 +1000 Subject: [PATCH 1/4] First batch of files to merge --- APSIM.bib | 99 ++++++++++++++++++ ApsimNG/Views/DirectedGraphView.cs | 104 +++++++++++++++---- Models/Core/Attributes/LinkAttribute.cs | 6 ++ Models/Functions/ExpressionFunction.cs | 9 ++ Models/Functions/StringComparisonFunction.cs | 83 +++++++++++++++ Models/Models.csproj | 1 + 6 files changed, 281 insertions(+), 21 deletions(-) create mode 100644 Models/Functions/StringComparisonFunction.cs diff --git a/APSIM.bib b/APSIM.bib index 21ee39ab1b..087fae3c72 100644 --- a/APSIM.bib +++ b/APSIM.bib @@ -16856,3 +16856,102 @@ @article{rubilar2018advances year={2018}, publisher={Springer} } + +@article{Young2009_HudsonCarbon, +Author = {Young, R. R. and Wilson, B. and Harden, S. and Bernardi, A.}, +Title = {{Accumulation of soil carbon under zero tillage cropping and perennial + vegetation on the Liverpool Plains, eastern Australia}}, +Journal = {{AUSTRALIAN JOURNAL OF SOIL RESEARCH}}, +Year = {{2009}}, +Volume = {{47}}, +Number = {{3}}, +Pages = {{273-285}}, +Publisher = {{CSIRO PUBLISHING}}, +Address = {{150 OXFORD ST, PO BOX 1139, COLLINGWOOD, VICTORIA 3066, AUSTRALIA}}, +Type = {{Article}}, +Language = {{English}}, +Affiliation = {{Young, RR (Reprint Author), Tamworth Agr Inst, NSW Dept Primary Ind, 4 Marsden Pk Rd, Calala, NSW 2340, Australia. + Young, R. R.; Harden, S.; Bernardi, A., Tamworth Agr Inst, NSW Dept Primary Ind, Calala, NSW 2340, Australia. + Wilson, B., Univ New England, NSW Dept Environm \& Climate Change, Armidale, NSW 2351, Australia. + Wilson, B., Univ New England, Sch Environm \& Rural Sci, Armidale, NSW 2351, Australia.}}, +DOI = {{10.1071/SR08104}}, +ISSN = {{0004-9573}}, +Keywords-Plus = {{NEW-SOUTH-WALES; ORGANIC-CARBON; NO-TILL; CONSERVATION TILLAGE; + SUBTROPICAL ACRISOL; STUBBLE MANAGEMENT; SEQUESTRATION; NITROGEN; + SYSTEMS; MATTER}}, +Research-Areas = {{Agriculture}}, +Web-of-Science-Categories = {{Soil Science}}, +Author-Email = {{rick.young@dpi.nsw.gov.au}}, +ResearcherID-Numbers = {{Wilson, Brian/G-4996-2011 + Management, Ecosystem/C-2259-2011 + }}, +Journal-ISO = {{Aust. J. Soil Res.}}, +} + +@article{Rolston1984, + author = {Rolston, D.E.; Rao, P.S.C.; Davidson, J.M.; Jessup, R.E.}, + title = {Simulation of denitrification losses of Nitrate fertiliser applied to uncropped, cropped, and manure-amended field plots}, + journal = {Soil Science}, + volume = {137}, + number = {270-278}, + year = {1984}, + type = {Journal Article} +} +@article{Reddy1980, + author = {Reddy, K.R.; Khaleel, R.; Overcash, M.R.}, + title = {Carbon transformations in land areas receiving organic wastes in relation to nonpoint source pollution: A conceptual model}, + journal = {Journal of Environmental Quality}, + volume = {9}, + number = {434-442}, + year = {1980}, + type = {Journal Article} +} + +@article{Schultz1995, + author = {Schultz, J.E.}, + title = {Crop production in a rotation trial at Tarlee, South Australia}, + journal = {Australian Journal of Experimental Agriculture}, + volume = {35}, + number = {865-876}, + year = {1995}, + type = {Journal Article} +} + +@article{Skjemstad2004, + author = {Skjemstad, J.O., Spouncer, L.R., Cowie, B., Swift, R.S.}, + title = {Calibration of the Rothamsted organic carbon turnover model (RothC ver.26.3), using measurable soil organic carbon pools}, + journal = {Australian Journal of Soil Research}, + volume = {42}, + number = {79-88}, + year = {2004}, + type = {Journal Article} +} + +@article{PARTON1998, +title = "DAYCENT and its land surface submodel: description and testing", +journal = "Global and Planetary Change", +volume = "19", +number = "1", +pages = "35 - 48", +year = "1998", +issn = "0921-8181", +doi = "https://doi.org/10.1016/S0921-8181(98)00040-X", +url = "http://www.sciencedirect.com/science/article/pii/S092181819800040X", +author = "William J. Parton and Melannie Hartman and Dennis Ojima and David Schimel", +keywords = "soil water, ecological models, soil temperature, latent heat flux, DAYCENT, water flow, trace gas flux" +} + +@article{THORBURN2010, +title = "Using the APSIM model to estimate nitrous oxide emissions from diverse Australian sugarcane production systems", +journal = "Agriculture, Ecosystems & Environment", +volume = "136", +number = "3", +pages = "343 - 350", +year = "2010", +note = "Estimation of nitrous oxide emission from ecosystems and its mitigation technologies", +issn = "0167-8809", +doi = "https://doi.org/10.1016/j.agee.2009.12.014", +url = "http://www.sciencedirect.com/science/article/pii/S0167880909003740", +author = "P.J. Thorburn and J.S. Biggs and K. Collins and M.E. Probert", +keywords = "Crop residues, Denitrification, Nitrification, Greenhouse gas, Nitrogen fertiliser, Irrigation" +} diff --git a/ApsimNG/Views/DirectedGraphView.cs b/ApsimNG/Views/DirectedGraphView.cs index 1cf82813e9..e427543899 100644 --- a/ApsimNG/Views/DirectedGraphView.cs +++ b/ApsimNG/Views/DirectedGraphView.cs @@ -13,6 +13,7 @@ namespace UserInterface.Views using System.Collections.Generic; using System.Drawing; using System.IO; + using System.Linq; /// /// A view that contains a graph and click zones for the user to allow @@ -20,28 +21,62 @@ namespace UserInterface.Views /// public class DirectedGraphView : ViewBase { + /// + /// The currently selected node/object. + /// private DGObject selectedObject; + + /// + /// Keeps track of whether the mouse button is currently down. + /// private bool mouseDown = false; + + /// + /// Drawing area upon which the graph is rendered. + /// + private DrawingArea drawable; + + /// + /// Position of the last moved node. + /// private PointD lastPos; - private List Nodes = new List(); - private List Arcs = new List(); + + /// + /// List of nodes. These are currently circles with text in them. + /// + private List nodes = new List(); + + /// + /// List of arcs which connect the nodes. + /// + private List arcs = new List(); /// Initializes a new instance of the class. public DirectedGraphView(ViewBase owner = null) : base(owner) { - DrawingArea drawingArea = new DrawingArea(); - - drawingArea.AddEvents( + drawable = new DrawingArea(); + + drawable.AddEvents( (int)Gdk.EventMask.PointerMotionMask | (int)Gdk.EventMask.ButtonPressMask | (int)Gdk.EventMask.ButtonReleaseMask); - drawingArea.ExposeEvent += OnDrawingAreaExpose; - drawingArea.ButtonPressEvent += OnMouseButtonPress; - drawingArea.ButtonReleaseEvent += OnMouseButtonRelease; ; - drawingArea.MotionNotifyEvent += OnMouseMove; ; - _mainWidget = drawingArea; - drawingArea.ModifyBg(StateType.Normal, new Gdk.Color(255, 255, 255)); + drawable.ExposeEvent += OnDrawingAreaExpose; + drawable.ButtonPressEvent += OnMouseButtonPress; + drawable.ButtonReleaseEvent += OnMouseButtonRelease; + drawable.MotionNotifyEvent += OnMouseMove; + + ScrolledWindow scroller = new ScrolledWindow(new Adjustment(0, 0, 100, 1, 1, 1), new Adjustment(0, 0, 100, 1, 1, 1)) + { + HscrollbarPolicy = PolicyType.Always, + VscrollbarPolicy = PolicyType.Always + }; + + scroller.AddWithViewport(drawable); + + _mainWidget = scroller; + drawable.ModifyBg(StateType.Normal, new Gdk.Color(255, 255, 255)); + drawable.Realized += OnRealized; } /// The description (nodes & arcs) of the directed graph. @@ -50,14 +85,14 @@ public DirectedGraph DirectedGraph get { DirectedGraph graph = new DirectedGraph(); - Nodes.ForEach(node => graph.Nodes.Add(node.ToNode())); - Arcs.ForEach(arc => graph.Arcs.Add(arc.ToArc())); + nodes.ForEach(node => graph.Nodes.Add(node.ToNode())); + arcs.ForEach(arc => graph.Arcs.Add(arc.ToArc())); return graph; } set { - value.Nodes.ForEach(node => Nodes.Add(new DGNode(node))); - value.Arcs.ForEach(arc => Arcs.Add(new DGArc(arc, Nodes))); + value.Nodes.ForEach(node => nodes.Add(new DGNode(node))); + value.Arcs.ForEach(arc => arcs.Add(new DGArc(arc, nodes))); } } @@ -67,7 +102,7 @@ public System.Drawing.Image Export() int width; int height; MainWidget.GdkWindow.GetSize(out width, out height); - Gdk.Pixbuf screenshot = Gdk.Pixbuf.FromDrawable(MainWidget.GdkWindow, MainWidget.Colormap, 0, 0, 0, 0, width, height); + Gdk.Pixbuf screenshot = Gdk.Pixbuf.FromDrawable(drawable.GdkWindow, drawable.Colormap, 0, 0, 0, 0, width - 20, height - 20); byte[] buffer = screenshot.SaveToBuffer("png"); MemoryStream stream = new MemoryStream(buffer); System.Drawing.Bitmap bitmap = new Bitmap(stream); @@ -81,9 +116,9 @@ private void OnDrawingAreaExpose(object sender, ExposeEventArgs args) Cairo.Context context = Gdk.CairoHelper.Create(area.GdkWindow); - foreach (DGArc tmpArc in Arcs) + foreach (DGArc tmpArc in arcs) tmpArc.Paint(context); - foreach (DGNode tmpNode in Nodes) + foreach (DGNode tmpNode in nodes) tmpNode.Paint(context); ((IDisposable)context.Target).Dispose(); @@ -101,11 +136,11 @@ private void OnMouseButtonPress(object o, ButtonPressEventArgs args) selectedObject.Selected = false; // Look through nodes for the click point - selectedObject = Nodes.FindLast(node => node.HitTest(clickPoint)); + selectedObject = nodes.FindLast(node => node.HitTest(clickPoint)); // If not found, look through arcs for the click point if (selectedObject == null) - selectedObject = Arcs.FindLast(arc => arc.HitTest(clickPoint)); + selectedObject = arcs.FindLast(arc => arc.HitTest(clickPoint)); // If found object, select it. if (selectedObject != null) @@ -140,8 +175,35 @@ private void OnMouseMove(object o, MotionNotifyEventArgs args) private void OnMouseButtonRelease(object o, ButtonReleaseEventArgs args) { mouseDown = false; + CheckSizing(); } - + /// + /// Drawing area has been rendered - make sure it has enough space. + /// + /// Sender object. + /// Event arguments. + private void OnRealized(object sender, EventArgs args) + { + CheckSizing(); + } + + /// + /// If the right-most node is out of the drawing area, doubles the width. + /// If the bottom-most node is out of the drawing area, doubles the height; + /// + private void CheckSizing() + { + if (nodes != null && nodes.Any()) + { + DGNode rightMostNode = nodes.Aggregate((node1, node2) => node1.Location.X > node2.Location.X ? node1 : node2); + DGNode bottomMostNode = nodes.Aggregate((node1, node2) => node1.Location.Y > node2.Location.Y ? node1 : node2); + if (rightMostNode.Location.X + rightMostNode.Width >= drawable.Allocation.Width) + drawable.WidthRequest = 2 * drawable.Allocation.Width; + // I Assume that the nodes are circles such that width = height. + if (bottomMostNode.Location.Y + bottomMostNode.Width >= drawable.Allocation.Height) + drawable.HeightRequest = 2 * drawable.Allocation.Height; + } + } } } diff --git a/Models/Core/Attributes/LinkAttribute.cs b/Models/Core/Attributes/LinkAttribute.cs index 4106adccc5..c93f4fce49 100644 --- a/Models/Core/Attributes/LinkAttribute.cs +++ b/Models/Core/Attributes/LinkAttribute.cs @@ -57,6 +57,12 @@ public class LinkByPathAttribute : LinkAttribute { /// The path to use to find a link match. public string Path { get; set; } + + /// Should the fields name be used when matching? + public override bool UseNameToMatch(IVariable field) + { + return false; + } } /// diff --git a/Models/Functions/ExpressionFunction.cs b/Models/Functions/ExpressionFunction.cs index 7c83b3fbf5..25db21a389 100644 --- a/Models/Functions/ExpressionFunction.cs +++ b/Models/Functions/ExpressionFunction.cs @@ -168,9 +168,18 @@ public void Document(List tags, int headingLevel, int in { // add a heading. tags.Add(new AutoDocumentation.Heading(Name, headingLevel)); + // write memos. + foreach (IModel memo in Apsim.Children(this, typeof(Memo))) + AutoDocumentation.DocumentModel(memo, tags, headingLevel + 1, indent); + + string st = Expression.Replace(".Value()", ""); st = st.Replace("*", "x"); tags.Add(new AutoDocumentation.Paragraph(Name + " = " + st, indent)); + + foreach (IModel child in Apsim.Children(this, typeof(IFunction))) + AutoDocumentation.DocumentModel(child, tags, headingLevel + 1, indent + 1); + } } diff --git a/Models/Functions/StringComparisonFunction.cs b/Models/Functions/StringComparisonFunction.cs new file mode 100644 index 0000000000..70dd65bd8b --- /dev/null +++ b/Models/Functions/StringComparisonFunction.cs @@ -0,0 +1,83 @@ +using System; +using System.Collections.Generic; +using Models.Core; + + +namespace Models.Functions +{ + /// Value returned is determined according to given criteria + [Serializable] + [Description("Tests if value of a string property is equal to a given value and returns a value depending on the result.")] + [ViewName("UserInterface.Views.GridView")] + [PresenterName("UserInterface.Presenters.PropertyPresenter")] + public class StringComparisonFunction : Model, IFunction, ICustomDocumentation + { + + /// The propertyname + [Description("Name of string property to compare")] + public string PropertyName { get; set; } + + /// The string value + [Description("Text string for comparison to the property value")] + public string StringValue { get; set; } + + /// The True Value + [Link] + IFunction TrueValue = null; + + /// The False Value + [Link] + IFunction FalseValue = null; + + [Link] + private ILocator locator = null; + + /// Gets the value. + /// The value. + public double Value(int arrayIndex = -1) + { + object s = locator.Get(PropertyName); + + string PropertyString; + if (s == null) + PropertyString = ""; + else if (s is Array) + PropertyString = (string)(s as Array).GetValue(arrayIndex); + else if (s is IFunction) + PropertyString = (s as IFunction).Value(arrayIndex).ToString(); + else + PropertyString = (string)s; + + bool stringCompareTrue = PropertyString.Equals(StringValue, StringComparison.CurrentCultureIgnoreCase); + + if (stringCompareTrue) + return TrueValue.Value(arrayIndex); + else + return FalseValue.Value(arrayIndex); + } + + /// Writes documentation for this function by adding to the list of documentation tags. + /// The list of tags to add to. + /// The level (e.g. H2) of the headings. + /// The level of indentation 1, 2, 3 etc. + public void Document(List tags, int headingLevel, int indent) + { + if (IncludeInDocumentation) + { + + // add a heading. + tags.Add(new AutoDocumentation.Heading(Name, headingLevel)); + + // write memos. + foreach (IModel memo in Apsim.Children(this, typeof(Memo))) + AutoDocumentation.DocumentModel(memo, tags, headingLevel + 1, indent); + + tags.Add(new AutoDocumentation.Paragraph("If " + PropertyName + " = " + StringValue + " Then", indent)); + AutoDocumentation.DocumentModel(TrueValue as IModel,tags, headingLevel+1, indent+1); + + tags.Add(new AutoDocumentation.Paragraph("Else", indent)); + AutoDocumentation.DocumentModel(FalseValue as IModel, tags, headingLevel+1, indent+1); + } + } + } +} \ No newline at end of file diff --git a/Models/Models.csproj b/Models/Models.csproj index 662b479fab..cdb504007e 100644 --- a/Models/Models.csproj +++ b/Models/Models.csproj @@ -197,6 +197,7 @@ + From 6db25ab29d5330a19442d3149c1efdbaeeecd266 Mon Sep 17 00:00:00 2001 From: "NEXUS\\hut104" Date: Tue, 6 Nov 2018 13:03:21 +1000 Subject: [PATCH 2/4] second batch --- Models/AgPasture/AgPasture.PastureSpecies.Organs.cs | 4 ++-- Models/Interfaces/INutrient.cs | 4 ++++ Models/Soils/Nutrient/Nutrient.cs | 2 +- 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/Models/AgPasture/AgPasture.PastureSpecies.Organs.cs b/Models/AgPasture/AgPasture.PastureSpecies.Organs.cs index 5cf6a52621..6e37483467 100644 --- a/Models/AgPasture/AgPasture.PastureSpecies.Organs.cs +++ b/Models/AgPasture/AgPasture.PastureSpecies.Organs.cs @@ -458,7 +458,7 @@ public class PastureBelowGroundOrgan public Soil mySoil = null; /// Soil nitrogen model. - private SoilNitrogen SoilNitrogen; + private INutrient SoilNitrogen; /// The solute manager in this zone public SoluteManager solutes = null; @@ -540,7 +540,7 @@ public PastureBelowGroundOrgan(string nameOfSpecies, int numTissues, if (solutes == null) throw new Exception("Cannot find solute manager in zone"); - SoilNitrogen = Apsim.Find(mySoil, typeof(SoilNitrogen)) as SoilNitrogen; + SoilNitrogen = Apsim.Find(mySoil, typeof(INutrient)) as INutrient; if (SoilNitrogen == null) throw new Exception("Cannot find SoilNitrogen in zone"); diff --git a/Models/Interfaces/INutrient.cs b/Models/Interfaces/INutrient.cs index 5ad77842c0..dbadef8ec3 100644 --- a/Models/Interfaces/INutrient.cs +++ b/Models/Interfaces/INutrient.cs @@ -20,5 +20,9 @@ public interface INutrient /// Calculate actual decomposition /// SurfaceOrganicMatterDecompType CalculateActualSOMDecomp(); + /// + /// Incorporate FOM + /// + void DoIncorpFOM(FOMLayerType FOMdata); } } diff --git a/Models/Soils/Nutrient/Nutrient.cs b/Models/Soils/Nutrient/Nutrient.cs index ca742d2920..d0b8dad643 100644 --- a/Models/Soils/Nutrient/Nutrient.cs +++ b/Models/Soils/Nutrient/Nutrient.cs @@ -155,7 +155,7 @@ public double[] FOMC /// Partition the given FOM C and N into fractions in each layer (one FOM) /// The in fo mdata. [EventSubscribe("IncorpFOM")] - private void OnIncorpFOM(FOMLayerType FOMdata) + public void DoIncorpFOM(FOMLayerType FOMdata) { // + Purpose: // Partition the given FOM C and N into fractions in each layer. From e22738a2c2f1ca787dbc8b87bee333c8aa109808 Mon Sep 17 00:00:00 2001 From: "NEXUS\\hut104" Date: Tue, 6 Nov 2018 13:38:10 +1000 Subject: [PATCH 3/4] Unwind last changes --- Models/AgPasture/AgPasture.PastureSpecies.Organs.cs | 4 ++-- Models/Interfaces/INutrient.cs | 4 ---- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/Models/AgPasture/AgPasture.PastureSpecies.Organs.cs b/Models/AgPasture/AgPasture.PastureSpecies.Organs.cs index 6e37483467..5cf6a52621 100644 --- a/Models/AgPasture/AgPasture.PastureSpecies.Organs.cs +++ b/Models/AgPasture/AgPasture.PastureSpecies.Organs.cs @@ -458,7 +458,7 @@ public class PastureBelowGroundOrgan public Soil mySoil = null; /// Soil nitrogen model. - private INutrient SoilNitrogen; + private SoilNitrogen SoilNitrogen; /// The solute manager in this zone public SoluteManager solutes = null; @@ -540,7 +540,7 @@ public PastureBelowGroundOrgan(string nameOfSpecies, int numTissues, if (solutes == null) throw new Exception("Cannot find solute manager in zone"); - SoilNitrogen = Apsim.Find(mySoil, typeof(INutrient)) as INutrient; + SoilNitrogen = Apsim.Find(mySoil, typeof(SoilNitrogen)) as SoilNitrogen; if (SoilNitrogen == null) throw new Exception("Cannot find SoilNitrogen in zone"); diff --git a/Models/Interfaces/INutrient.cs b/Models/Interfaces/INutrient.cs index dbadef8ec3..5ad77842c0 100644 --- a/Models/Interfaces/INutrient.cs +++ b/Models/Interfaces/INutrient.cs @@ -20,9 +20,5 @@ public interface INutrient /// Calculate actual decomposition /// SurfaceOrganicMatterDecompType CalculateActualSOMDecomp(); - /// - /// Incorporate FOM - /// - void DoIncorpFOM(FOMLayerType FOMdata); } } From 6370e8a791b4f07e791ce5b84c0d34999d5e0da9 Mon Sep 17 00:00:00 2001 From: "NEXUS\\hut104" Date: Tue, 6 Nov 2018 14:01:40 +1000 Subject: [PATCH 4/4] Unwind again --- Models/Soils/Nutrient/Nutrient.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Models/Soils/Nutrient/Nutrient.cs b/Models/Soils/Nutrient/Nutrient.cs index d0b8dad643..ca742d2920 100644 --- a/Models/Soils/Nutrient/Nutrient.cs +++ b/Models/Soils/Nutrient/Nutrient.cs @@ -155,7 +155,7 @@ public double[] FOMC /// Partition the given FOM C and N into fractions in each layer (one FOM) /// The in fo mdata. [EventSubscribe("IncorpFOM")] - public void DoIncorpFOM(FOMLayerType FOMdata) + private void OnIncorpFOM(FOMLayerType FOMdata) { // + Purpose: // Partition the given FOM C and N into fractions in each layer.