Example #1
0
        /// <summary>
        /// Document the model.
        /// </summary>
        public override IEnumerable <ITag> Document()
        {
            // Add heading and description.
            foreach (ITag tag in base.Document())
            {
                yield return(tag);
            }

            // List the parameters, properties, and processes from this organ that need to be documented:

            // Document DM demands.
            yield return(new Section("Dry Matter Demand", DocumentDMDemand(dmDemands)));

            // Document N demands.
            yield return(new Section("Nitrogen Demand", DocumentNDemand(FindChild("nDemands"))));

            // Document N concentration thresholds.
            yield return(new Section("N Concentration Thresholds", DocumentNConcentrationThresholds()));

            // todo: should this be in its own section?
            IModel nDemandSwitch = FindChild("NitrogenDemandSwitch");

            if (nDemandSwitch is Constant nDemandConst)
            {
                if (nDemandConst.Value() == 1)
                {
                    // Don't bother documenting as it does nothing
                }
                else
                {
                    yield return(new Paragraph($"The demand for N is reduced by a factor of {nDemandConst.Value()} as specified by the NitrogenDemandSwitch"));
                }
            }
            else
            {
                yield return(new Paragraph("The demand for N is reduced by a factor specified by the NitrogenDemandSwitch."));

                foreach (ITag tag in nDemandSwitch.Document())
                {
                    yield return(tag);
                }
            }

            // Document DM supplies.
            yield return(new Section("Dry Matter Supply", DocumentDMSupply()));

            // Document N supplies.
            yield return(new Section("Nitrogen Supply", DocumentNSupply()));

            // Document senescence and detachment.
            yield return(new Section("Senescence and Detachment", DocumentSenescence()));

            if (biomassRemovalModel != null)
            {
                foreach (ITag tag in biomassRemovalModel.Document())
                {
                    yield return(tag);
                }
            }
        }
Example #2
0
        /// <summary>Writes documentation for this function by adding to the list of documentation tags.</summary>
        /// <param name="tags">The list of tags to add to.</param>
        /// <param name="headingLevel">The level (e.g. H2) of the headings.</param>
        /// <param name="indent">The level of indentation 1, 2, 3 etc.</param>
        public void Document(List <AutoDocumentation.ITag> tags, int headingLevel, int indent)
        {
            if (IncludeInDocumentation)
            {
                // add a heading, the name of this organ
                tags.Add(new AutoDocumentation.Heading(Name, headingLevel));

                // write the basic description of this class, given in the <summary>
                AutoDocumentation.DocumentModelSummary(this, tags, headingLevel, indent, false);

                // write the memos
                foreach (IModel memo in Apsim.Children(this, typeof(Memo)))
                {
                    AutoDocumentation.DocumentModel(memo, tags, headingLevel + 1, indent);
                }

                //// List the parameters, properties, and processes from this organ that need to be documented:

                // document DM demands
                tags.Add(new AutoDocumentation.Heading("Dry Matter Demand", headingLevel + 1));
                tags.Add(new AutoDocumentation.Paragraph("The dry matter demand for the organ is calculated as defined in DMDemands, based on the DMDemandFunction and partition fractions for each biomass pool.", indent));
                IModel DMDemand = Apsim.Child(this, "dmDemands");
                AutoDocumentation.DocumentModel(DMDemand, tags, headingLevel + 2, indent);

                // document N demands
                tags.Add(new AutoDocumentation.Heading("Nitrogen Demand", headingLevel + 1));
                tags.Add(new AutoDocumentation.Paragraph("The N demand is calculated as defined in NDemands, based on DM demand the N concentration of each biomass pool.", indent));
                IModel NDemand = Apsim.Child(this, "nDemands");
                AutoDocumentation.DocumentModel(NDemand, tags, headingLevel + 2, indent);

                // document N concentration thresholds
                IModel MinN = Apsim.Child(this, "MinimumNConc");
                AutoDocumentation.DocumentModel(MinN, tags, headingLevel + 2, indent);
                IModel CritN = Apsim.Child(this, "CriticalNConc");
                AutoDocumentation.DocumentModel(CritN, tags, headingLevel + 2, indent);
                IModel MaxN = Apsim.Child(this, "MaximumNConc");
                AutoDocumentation.DocumentModel(MaxN, tags, headingLevel + 2, indent);
                IModel NDemSwitch = Apsim.Child(this, "NitrogenDemandSwitch");
                if (NDemSwitch is Constant)
                {
                    if ((NDemSwitch as Constant).Value() == 1.0)
                    {
                        //Don't bother documenting as is does nothing
                    }
                    else
                    {
                        tags.Add(new AutoDocumentation.Paragraph("The demand for N is reduced by a factor of " + (NDemSwitch as Constant).Value() + " as specified by the NitrogenDemandSwitch", indent));
                    }
                }
                else
                {
                    tags.Add(new AutoDocumentation.Paragraph("The demand for N is reduced by a factor specified by the NitrogenDemandSwitch.", indent));
                    AutoDocumentation.DocumentModel(NDemSwitch, tags, headingLevel + 2, indent);
                }

                // document DM supplies
                tags.Add(new AutoDocumentation.Heading("Dry Matter Supply", headingLevel + 1));
                IModel DMReallocFac = Apsim.Child(this, "DMReallocationFactor");
                if (DMReallocFac is Constant)
                {
                    if ((DMReallocFac as Constant).Value() == 0)
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " does not reallocate DM when senescence of the organ occurs.", indent));
                    }
                    else
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " will reallocate " + (DMReallocFac as Constant).Value() * 100 + "% of DM that senesces each day.", indent));
                    }
                }
                else
                {
                    tags.Add(new AutoDocumentation.Paragraph("The proportion of senescing DM that is allocated each day is quantified by the DMReallocationFactor.", indent));
                    AutoDocumentation.DocumentModel(DMReallocFac, tags, headingLevel + 2, indent);
                }
                IModel DMRetransFac = Apsim.Child(this, "DMRetranslocationFactor");
                if (DMRetransFac is Constant)
                {
                    if ((DMRetransFac as Constant).Value() == 0)
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " does not retranslocate non-structural DM.", indent));
                    }
                    else
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " will retranslocate " + (DMRetransFac as Constant).Value() * 100 + "% of non-structural DM each day.", indent));
                    }
                }
                else
                {
                    tags.Add(new AutoDocumentation.Paragraph("The proportion of non-structural DM that is allocated each day is quantified by the DMReallocationFactor.", indent));
                    AutoDocumentation.DocumentModel(DMRetransFac, tags, headingLevel + 2, indent);
                }

                // document N supplies
                tags.Add(new AutoDocumentation.Heading("Nitrogen Supply", headingLevel + 1));
                IModel NReallocFac = Apsim.Child(this, "NReallocationFactor");
                if (NReallocFac is Constant)
                {
                    if ((NReallocFac as Constant).Value() == 0)
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " does not reallocate N when senescence of the organ occurs.", indent));
                    }
                    else
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " will reallocate " + (NReallocFac as Constant).Value() * 100 + "% of N that senesces each day.", indent));
                    }
                }
                else
                {
                    tags.Add(new AutoDocumentation.Paragraph("The proportion of senescing N that is allocated each day is quantified by the NReallocationFactor.", indent));
                    AutoDocumentation.DocumentModel(NReallocFac, tags, headingLevel + 2, indent);
                }
                IModel NRetransFac = Apsim.Child(this, "NRetranslocationFactor");
                if (NRetransFac is Constant)
                {
                    if ((NRetransFac as Constant).Value() == 0)
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " does not retranslocate non-structural N.", indent));
                    }
                    else
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " will retranslocate " + (NRetransFac as Constant).Value() * 100 + "% of non-structural N each day.", indent));
                    }
                }
                else
                {
                    tags.Add(new AutoDocumentation.Paragraph("The proportion of non-structural N that is allocated each day is quantified by the NReallocationFactor.", indent));
                    AutoDocumentation.DocumentModel(NRetransFac, tags, headingLevel + 2, indent);
                }

                // document senescence and detachment
                tags.Add(new AutoDocumentation.Heading("Senescence and Detachment", headingLevel + 1));
                IModel SenRate = Apsim.Child(this, "SenescenceRate");
                if (SenRate is Constant)
                {
                    if ((SenRate as Constant).Value() == 0)
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " has senescence parameterised to zero so all biomass in this organ will remain alive.", indent));
                    }
                    else
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " senesces " + (SenRate as Constant).Value() * 100 + "% of its live biomass each day, moving the corresponding amount of biomass from the live to the dead biomass pool.", indent));
                    }
                }
                else
                {
                    tags.Add(new AutoDocumentation.Paragraph("The proportion of live biomass that senesces and moves into the dead pool each day is quantified by the SenescenceRate.", indent));
                    AutoDocumentation.DocumentModel(SenRate, tags, headingLevel + 2, indent);
                }

                IModel DetRate = Apsim.Child(this, "DetachmentRateFunction");
                if (DetRate is Constant)
                {
                    if ((DetRate as Constant).Value() == 0)
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " has detachment parameterised to zero so all biomass in this organ will remain with the plant until a defoliation or harvest event occurs.", indent));
                    }
                    else
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " detaches " + (DetRate as Constant).Value() * 100 + "% of its live biomass each day, passing it to the surface organic matter model for decomposition.", indent));
                    }
                }
                else
                {
                    tags.Add(new AutoDocumentation.Paragraph("The proportion of Biomass that detaches and is passed to the surface organic matter model for decomposition is quantified by the DetachmentRateFunction.", indent));
                    AutoDocumentation.DocumentModel(DetRate, tags, headingLevel + 2, indent);
                }

                if (biomassRemovalModel != null)
                {
                    biomassRemovalModel.Document(tags, headingLevel + 1, indent);
                }
            }
        }
Example #3
0
        /// <summary>Writes documentation for this function by adding to the list of documentation tags.</summary>
        /// <param name="tags">The list of tags to add to.</param>
        /// <param name="headingLevel">The level (e.g. H2) of the headings.</param>
        /// <param name="indent">The level of indentation 1, 2, 3 etc.</param>
        public override void Document(List <AutoDocumentation.ITag> tags, int headingLevel, int indent)
        {
            if (IncludeInDocumentation && StructuralFraction != null)
            {
                // add a heading.
                tags.Add(new AutoDocumentation.Heading(Name, headingLevel));

                // write description of this class.
                AutoDocumentation.DocumentModel(this, tags, headingLevel, indent);

                // Documment DM demands.
                tags.Add(new AutoDocumentation.Heading("Dry Matter Demand", headingLevel + 1));
                tags.Add(new AutoDocumentation.Paragraph("Total Dry matter demand is calculated by the DMDemandFunction", indent));
                IModel DMDemand = Apsim.Child(this, "DMDemandFunction");
                DMDemand.Document(tags, -1, indent);
                IModel StrucFrac = Apsim.Child(this, "StructuralFraction");
                if (StrucFrac.GetType() == typeof(Constant))
                {
                    if (StructuralFraction.Value() == 1.0)
                    {
                        tags.Add(new AutoDocumentation.Paragraph("All demand is structural and this organ has no Non-structural demand", indent));
                    }
                    else
                    {
                        double StrucPercent = StructuralFraction.Value() * 100;
                        tags.Add(new AutoDocumentation.Paragraph("Of total biomass, " + StrucPercent + "% of this is structural and the remainder is non-structural demand", indent));
                        tags.Add(new AutoDocumentation.Paragraph("Any Non-structural Demand Capacity (StructuralWt/StructuralFraction) that is not currently occupied is also included in Non-structural DM Demand", indent));
                    }
                }
                else
                {
                    tags.Add(new AutoDocumentation.Paragraph("The proportion of total biomass that is partitioned to structural is determined by the StructuralFraction", indent));
                    StrucFrac.Document(tags, -1, indent);
                    tags.Add(new AutoDocumentation.Paragraph("Any Non-structural Demand Capacity (StructuralWt/StructuralFraction) that is not currently occupied is also included in Non-structural DM Demand", indent));
                }

                // Document Nitrogen Demand
                tags.Add(new AutoDocumentation.Heading("Nitrogen Demand", headingLevel + 1));
                tags.Add(new AutoDocumentation.Paragraph("The daily structural N demand is the product of Total DM demand and a Minimum N concentration", indent));
                IModel MinN = Apsim.Child(this, "MinimumNConc");
                MinN.Document(tags, -1, indent);
                tags.Add(new AutoDocumentation.Paragraph("The daily Storage N demand is the product of Total DM demand and a Maximum N concentration", indent));
                IModel MaxN = Apsim.Child(this, "MaximumNConc");
                MaxN.Document(tags, -1, indent);
                IModel NDemSwitch = Apsim.Child(this, "NitrogenDemandSwitch");
                if (NDemSwitch.GetType() == typeof(Constant))
                {
                    if (NitrogenDemandSwitch.Value() == 1.0)
                    {
                        //Dont bother docummenting as is does nothing
                    }
                    else
                    {
                        tags.Add(new AutoDocumentation.Paragraph("The demand for N is reduced by a factor of " + NitrogenDemandSwitch.Value() + " as specified by the NitrogenDemandFactor", indent));
                    }
                }
                else
                {
                    tags.Add(new AutoDocumentation.Paragraph("The demand for N is reduced by a factor specified by the NitrogenDemandFactor", indent));
                    NDemSwitch.Document(tags, -1, indent);
                }

                //Document DM supplies
                tags.Add(new AutoDocumentation.Heading("Dry Matter Supply", headingLevel + 1));
                IModel DMReallocFac = Apsim.Child(this, "DMReallocationFactor");
                if (DMReallocFac.GetType() == typeof(Constant))
                {
                    if (DMReallocationFactor.Value() == 0)
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " does not reallocate DM when senescence of the organ occurs", indent));
                    }
                    else
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " will reallocate " + DMReallocationFactor.Value() * 100 + "% of DM that senesces each day", indent));
                    }
                }
                else
                {
                    tags.Add(new AutoDocumentation.Paragraph("The proportion of senescing DM tha is allocated each day is quantified by the DMReallocationFactor", indent));
                    DMReallocFac.Document(tags, -1, indent);
                }
                IModel DMRetransFac = Apsim.Child(this, "DMRetranslocationFactor");
                if (DMRetransFac.GetType() == typeof(Constant))
                {
                    if (DMRetranslocationFactor.Value() == 0)
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " does not retranslocate non-structural DM", indent));
                    }
                    else
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " will retranslocate " + DMRetranslocationFactor.Value() * 100 + "% of non-structural DM each day", indent));
                    }
                }
                else
                {
                    tags.Add(new AutoDocumentation.Paragraph("The proportion of non-structural DM tha is allocated each day is quantified by the DMReallocationFactor", indent));
                    DMRetransFac.Document(tags, -1, indent);
                }

                //Document N supplies
                tags.Add(new AutoDocumentation.Heading("Nitrogen Supply", headingLevel + 1));
                IModel NReallocFac = Apsim.Child(this, "NReallocationFactor");
                if (NReallocFac.GetType() == typeof(Constant))
                {
                    if (NReallocationFactor.Value() == 0)
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " does not reallocate N when senescence of the organ occurs", indent));
                    }
                    else
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " will reallocate " + NReallocationFactor.Value() * 100 + "% of N that senesces each day", indent));
                    }
                }
                else
                {
                    tags.Add(new AutoDocumentation.Paragraph("The proportion of senescing N tha is allocated each day is quantified by the NReallocationFactor", indent));
                    NReallocFac.Document(tags, -1, indent);
                }
                IModel NRetransFac = Apsim.Child(this, "NRetranslocationFactor");
                if (NRetransFac.GetType() == typeof(Constant))
                {
                    if (NRetranslocationFactor.Value() == 0)
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " does not retranslocate non-structural N", indent));
                    }
                    else
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " will retranslocate " + NRetranslocationFactor.Value() * 100 + "% of non-structural N each day", indent));
                    }
                }
                else
                {
                    tags.Add(new AutoDocumentation.Paragraph("The proportion of non-structural N that is allocated each day is quantified by the NReallocationFactor", indent));
                    NRetransFac.Document(tags, -1, indent);
                }

                //Document Biomass Senescence and Detachment
                tags.Add(new AutoDocumentation.Heading("Senescence and Detachment", headingLevel + 1));
                IModel Sen = Apsim.Child(this, "SenescenceRate");
                if (Sen.GetType() == typeof(Constant))
                {
                    if (SenescenceRate.Value() == 0)
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " has senescence parameterised to zero so all biomss in this organ will remain live", indent));
                    }
                    else
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " senesces " + SenescenceRate.Value() * 100 + "% of its live biomass each day, moving the corresponding amount of biomass from the live to the dead biomass pool", indent));
                    }
                }
                else
                {
                    tags.Add(new AutoDocumentation.Paragraph("The proportion of live biomass that senesces and moves into the dead pool each day is quantified by the SenescenceFraction", indent));
                    Sen.Document(tags, -1, indent);
                }

                IModel Det = Apsim.Child(this, "DetachmentRateFunction");
                if (Sen.GetType() == typeof(Constant))
                {
                    if (DetachmentRateFunction.Value() == 0)
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " has detachment parameterised to zero so all biomss in this organ will remain with the plant until a defoliation or harvest event occurs", indent));
                    }
                    else
                    {
                        tags.Add(new AutoDocumentation.Paragraph(Name + " detaches " + DetachmentRateFunction.Value() * 100 + "% of its live biomass each day, passing it to the surface organic matter model for decomposition", indent));
                    }
                }
                else
                {
                    tags.Add(new AutoDocumentation.Paragraph("The proportion of Biomass that detaches and is passed to the surface organic matter model for decomposition is quantified by the DetachmentRateFunction", indent));
                    Det.Document(tags, -1, indent);
                }

                if (biomassRemovalModel != null)
                {
                    biomassRemovalModel.Document(tags, headingLevel + 1, indent);
                }
            }
        }
Example #4
0
        /// <summary>Writes documentation for this function by adding to the list of documentation tags.</summary>
        public override IEnumerable <ITag> Document()
        {
            foreach (var tag in GetModelDescription())
            {
                yield return(tag);
            }

            // Document memos.
            foreach (var memo in FindAllChildren <Memo>())
            {
                foreach (var tag in memo.Document())
                {
                    yield return(tag);
                }
            }

            // List the parameters, properties, and processes from this organ that need to be documented:

            yield return(new Section("Initial Dry Matter", initialWt.Document()));

            // document DM demands
            var dmDemandTags = new List <ITag>();

            dmDemandTags.Add(new Paragraph("The dry matter demand for the organ is calculated as defined in DMDemands, based on the DMDemandFunction and partition fractions for each biomass pool."));
            dmDemandTags.AddRange(dmDemands.Document());
            yield return(new Section("Dry Matter Demand", dmDemandTags));

            // document N demands
            var nDemandTags = new List <ITag>();

            nDemandTags.Add(new Paragraph("The N demand is calculated as defined in NDemands, based on DM demand the N concentration of each biomass pool."));
            nDemandTags.AddRange(nDemands.Document());
            yield return(new Section("Nitrogen Demand", nDemandTags));

            // document N demands
            var nConcTags = new List <ITag>();

            nConcTags.AddRange(minimumNConc.Document());
            nConcTags.AddRange(criticalNConc.Document());
            nConcTags.AddRange(maximumNConc.Document());
            yield return(new Section("Nitrogen Concentration Thresholds", nDemandTags));

            // document DM supplies
            var dmSupplyTags = new List <ITag>();

            dmSupplyTags.AddRange(dmReallocationFactor.Document());
            dmSupplyTags.AddRange(dmRetranslocationFactor.Document());
            yield return(new Section("Dry Matter Supply", dmSupplyTags));

            // document photosynthesis
            yield return(new Section("Photosynthesis", photosynthesis.Document()));

            // document N supplies
            var nSupplyTags = new List <ITag>();

            nSupplyTags.AddRange(nReallocationFactor.Document());
            nSupplyTags.AddRange(nRetranslocationFactor.Document());
            yield return(new Section("Nitrogen Supply", nSupplyTags));

            // document canopy
            var canopyTags = new List <ITag>();

            if (area != null)
            {
                canopyTags.Add(new Paragraph(Name + " has been defined with a LAIFunction, cover is calculated using the Beer-Lambert equation."));
                canopyTags.AddRange(area.Document());
            }
            if (cover != null)
            {
                canopyTags.Add(new Paragraph(Name + " has been defined with a CoverFunction. LAI is calculated using an inverted Beer-Lambert equation"));
                canopyTags.AddRange(cover.Document());
            }
            canopyTags.AddRange(extinctionCoefficient.Document());
            canopyTags.AddRange(tallness.Document());
            yield return(new Section("Canopy Properties", canopyTags));

            var stomatalConductanceTags = new List <ITag>();

            stomatalConductanceTags.Add(new Paragraph("Stomatal Conductance (gs) is calculated for use within the micromet model by adjusting a value provided for an atmospheric CO2 concentration of 350 ppm. The impact of other stresses  (e.g. Temperature, N) are captured through the modifier, Frgr."));
            stomatalConductanceTags.Add(new Paragraph("  gs = Gsmax350 x FRGR x stomatalConductanceCO2Modifier"));
            stomatalConductanceTags.AddRange(stomatalConductanceCO2Modifier.Document());
            yield return(new Section("StomatalConductance", stomatalConductanceTags));

            // document senescence and detachment
            var senescenceTags = new List <ITag>();

            if (senescenceRate is Constant senescenceRateConstant)
            {
                if (senescenceRateConstant.Value() == 0)
                {
                    senescenceTags.Add(new Paragraph(Name + " has senescence parameterised to zero so all biomass in this organ will remain alive."));
                }
                else
                {
                    senescenceTags.Add(new Paragraph(Name + " senesces " + senescenceRateConstant.Value() * 100 + "% of its live biomass each day, moving the corresponding amount of biomass from the live to the dead biomass pool."));
                }
            }
            else
            {
                senescenceTags.Add(new Paragraph("The proportion of live biomass that senesces and moves into the dead pool each day is quantified by the SenescenceRate."));
                senescenceTags.AddRange(senescenceRate.Document());
            }

            if (detachmentRate is Constant detachmentRateConstant)
            {
                if (detachmentRateConstant.Value() == 0)
                {
                    senescenceTags.Add(new Paragraph(Name + " has detachment parameterised to zero so all biomass in this organ will remain with the plant until a defoliation or harvest event occurs."));
                }
                else
                {
                    senescenceTags.Add(new Paragraph(Name + " detaches " + detachmentRateConstant.Value() * 100 + "% of its dead biomass each day, passing it to the surface organic matter model for decomposition."));
                }
            }
            else
            {
                senescenceTags.Add(new Paragraph("The proportion of Biomass that detaches and is passed to the surface organic matter model for decomposition is quantified by the DetachmentRateFunction."));
                senescenceTags.AddRange(detachmentRate.Document());
            }
            yield return(new Section("Senescence and Detachment", senescenceTags));

            if (biomassRemovalModel != null)
            {
                yield return(new Section("Biomass removal", biomassRemovalModel.Document()));
            }
        }
Example #5
0
        /// <summary>
        /// Document the model.
        /// </summary>
        public override IEnumerable <ITag> Document()
        {
            foreach (ITag tag in GetModelDescription())
            {
                yield return(tag);
            }

            // Document DM demands.
            yield return(new Section("Dry Matter Demand", DocumentDMDemands()));

            // Document N demands.
            yield return(new Section("Nitrogen Demand", DocumentNDemand()));

            // Document N concentration thresholds.
            // todo: Should these be in their own section?
            foreach (ITag tag in minimumNConc.Document())
            {
                yield return(tag);
            }

            foreach (ITag tag in criticalNConc.Document())
            {
                yield return(tag);
            }

            foreach (ITag tag in maximumNConc.Document())
            {
                yield return(tag);
            }

            IModel nDemandSwitch = FindChild("NitrogenDemandSwitch");

            if (nDemandSwitch != null)
            {
                if (nDemandSwitch is Constant nDemandConst)
                {
                    if (nDemandConst.Value() == 1)
                    {
                        //Don't bother documenting as is does nothing
                    }
                    else
                    {
                        yield return(new Paragraph($"The demand for N is reduced by a factor of {nDemandConst.Value()} as specified by the NitrogenDemandSwitch"));
                    }
                }
                else
                {
                    yield return(new Paragraph("The demand for N is reduced by a factor specified by the NitrogenDemandSwitch."));

                    foreach (ITag tag in nDemandSwitch.Document())
                    {
                        yield return(tag);
                    }
                }
            }

            // document DM supplies
            yield return(new Section("Dry Matter Supply", DocumentDMSupply()));


            // Document N supplies.
            yield return(new Section("Nitrogen Supply", DocumentNSupply()));


            // Document N fixation.
            IModel fixationRate = FindChild("FixationRate");

            if (fixationRate != null)
            {
                foreach (ITag tag in fixationRate.Document())
                {
                    yield return(tag);
                }
            }

            // Document senescence and detachment.
            yield return(new Section("Senescence and Detachment", DocumentSenescenceRate()));

            if (biomassRemovalModel != null)
            {
                foreach (ITag tag in biomassRemovalModel.Document())
                {
                    yield return(tag);
                }
            }
        }