11 - AgriPV Systems#

This journal shows how to model an AgriPV site, calculating the irradiance not only on the modules but also the irradiance received by the ground to evaluate available solar ersource for plants.

We assume that bifacia_radiacne is already installed in your computer. This works for bifacial_radiance v.3 release.

These journal outlines 4 useful uses of bifacial_radiance and some tricks:

  • Creating the modules in the AgriPV site

  • Adding extra geometry for the pillars/posts supporting the AgriPV site

  • Hacking the sensors to sample the ground irradiance and create irradiance map

  • Adding object to simulate variations in ground albedo from different crops between rows.

Steps:#

  1. Generate the geometry

  2. Analyse the Ground Irradiance

  3. Analyse and MAP the Ground Irradiance

  4. Adding different Albedo Section

Preview of what we will create:#

Another view AgriPV Image We will create And this is how it will look like:

AgriPV modeled step 4

1. Generate the geometry#

This section goes from setting up variables to making the OCT axis. We are also adding some custom elements for the torquetubes and posts.

We’ve done this before a couple times, no new stuff here.

The magic is that, for doing the carport we see in the figure, we are going to do a 4-up configuration of modules (numpanels), and we are going to repeat that 3-UP 6 times (nMods)

[1]:
import os
from pathlib import Path

testfolder = str(Path().resolve().parent.parent / 'bifacial_radiance' / 'TEMP' /  'Tutorial_11')

if not os.path.exists(testfolder):
    os.makedirs(testfolder)

print ("Your simulation will be stored in %s" % testfolder)
Your simulation will be stored in C:\Users\sayala\Documents\GitHub\bifacial_radiance\bifacial_radiance\TEMP\Tutorial_11
[2]:
from bifacial_radiance import *
import numpy as np
import pandas as pd
[3]:
simulationname = 'tutorial_11'

#Location:
lat = 40.0583  # NJ
lon = -74.4057  # NJ

# MakeModule Parameters
moduletype='test-module'
numpanels = 3  # AgriPV site has 3 modules along the y direction (N-S since we are facing it to the south) .
x = 0.95
y = 1.95
xgap = 2.0# Leaving 15 centimeters between modules on x direction
ygap = 0.10 # Leaving 10 centimeters between modules on y direction
zgap = 0 # no gap to torquetube.
sensorsy = 6*numpanels  # this will give 6 sensors per module, 1 per cell

# Other default values:

# TorqueTube Parameters
axisofrotationTorqueTube=False  # this is False by default if there is no torquetbue parameters
torqueTube = False
cellLevelModule = True

numcellsx = 12
numcellsy = 6
xcell = 0.156
ycell = 0.156
xcellgap = 0.02
ycellgap = 0.02

cellLevelModuleParams = {'numcellsx': numcellsx, 'numcellsy':numcellsy,
                         'xcell': xcell, 'ycell': ycell, 'xcellgap': xcellgap, 'ycellgap': ycellgap}

# SceneDict Parameters
pitch = 15 # m
albedo = 0.2  #'grass'     # ground albedo
hub_height = 4.3 # m
nMods = 6 # six modules per row.
nRows = 3  # 3 row

azimuth_ang=180 # Facing south
tilt =35 # tilt.

# Now let's run the example

demo = RadianceObj(simulationname,path = testfolder)
demo.setGround(albedo)
epwfile = demo.getEPW(lat, lon) # NJ lat/lon 40.0583° N, 74.4057
metdata = demo.readWeatherFile(epwfile, coerce_year=2001)
timestamp = metdata.datetime.index(pd.to_datetime('2001-06-17 13:0:0 -5'))  # Make this timezone aware, use -5 for EST.
demo.gendaylit(timestamp)


# Making module with all the variables
module=demo.makeModule(name=moduletype,x=x,y=y,numpanels=numpanels,
                           xgap=xgap, ygap=ygap, cellModule=cellLevelModuleParams)
# create a scene with all the variables
sceneDict = {'tilt':tilt,'pitch': 15,'hub_height':hub_height,'azimuth':azimuth_ang, 'nMods': nMods, 'nRows': nRows}
scene = demo.makeScene(module=moduletype, sceneDict=sceneDict)
octfile = demo.makeOct(demo.getfilelist())

path = C:\Users\sayala\Documents\GitHub\bifacial_radiance\bifacial_radiance\TEMP\Tutorial_11
Making path: images
Making path: objects
Making path: results
Making path: skies
Making path: EPWs
Making path: materials
Loading albedo, 1 value(s), 0.200 avg
1 nonzero albedo values.
Getting weather file: USA_NJ_McGuire.AFB.724096_TMY3.epw
 ... OK!
8760 line in WeatherFile. Assuming this is a standard hourly WeatherFile for the year for purposes of saving Gencumulativesky temporary weather files in EPW folder.
Coercing year to 2001
Saving file EPWs\metdata_temp.csv, # points: 8760
Calculating Sun position for Metdata that is right-labeled  with a delta of -30 mins. i.e. 12 is 11:30 sunpos

Module Name: test-module
Module was shifted by 0.078 in X to avoid sensors on air
This is a Cell-Level detailed module with Packaging Factor of 0.81 %
Module test-module updated in module.json
Created tutorial_11.oct

If desired, you can view the Oct file at this point:

*rvu -vf views:nbsphinx-math:`front`.vp -e .01 tutorial_11.oct*

[6]:

## Comment the ! line below to run rvu from the Jupyter notebook instead of your terminal. ## Simulation will stop until you close the rvu window #!rvu -vf views\front.vp -e .01 tutorial_11.oct

And adjust the view parameters, you should see this image.

AgriPV modeled step 1

Adding the structure#

We will add on the torquetube and pillars.

Positions of the piles could be done more programatically, but they are kinda estimated at the moment.

[12]:
torquetubelength = module.scenex*(nMods)

name='Post1'
text='! genbox Metal_Aluminum_Anodized torquetube_row1 {} 0.2 0.3 | xform -t {} -0.1 -0.3 | xform -t 0 0 4.2'.format(
                                                    torquetubelength, (-torquetubelength+module.sceney)/2.0)
customObject = demo.makeCustomObject(name,text)
demo.appendtoScene(radfile=scene.radfiles, customObject=customObject, text="!xform -rz 0")

name='Post2'
text='! genbox Metal_Aluminum_Anodized torquetube_row2 {} 0.2 0.3 | xform -t {} -0.1 -0.3 | xform -t 0 15 4.2'.format(
                                            torquetubelength, (-torquetubelength+module.sceney)/2.0)
customObject = demo.makeCustomObject(name,text)
demo.appendtoScene(radfile=scene.radfiles, customObject=customObject, text="!xform -rz 0")

name='Post3'
text='! genbox Metal_Aluminum_Anodized torquetube_row2 {} 0.2 0.3 | xform -t {} -0.1 -0.3 | xform -t 0 -15 4.2'.format(
                                              torquetubelength, (-torquetubelength+module.sceney)/2.0)
customObject = demo.makeCustomObject(name,text)
demo.appendtoScene(radfile=scene.radfiles, customObject=customObject, text="!xform -rz 0")


Custom Object Name objects\Post1.rad

Custom Object Name objects\Post2.rad

Custom Object Name objects\Post3.rad
[13]:
name='Pile'
pile1x = (torquetubelength+module.sceney)/2.0
pilesep = pile1x*2.0/7.0

text= '! genrev Metal_Grey tube1row1 t*4.2 0.15 32 | xform -t {} 0 0'.format(pile1x)
text += '\r\n! genrev Metal_Grey tube1row2 t*4.2 0.15 32 | xform -t {} 15 0'.format(pile1x)
text += '\r\n! genrev Metal_Grey tube1row3 t*4.2 0.15 32 | xform -t {} -15 0'.format(pile1x)

for i in range (1, 7):
    text += '\r\n! genrev Metal_Grey tube{}row1 t*4.2 0.15 32 | xform -t {} 0 0'.format(i+1, pile1x-pilesep*i)
    text += '\r\n! genrev Metal_Grey tube{}row2 t*4.2 0.15 32 | xform -t {} 15 0'.format(i+1, pile1x-pilesep*i)
    text += '\r\n! genrev Metal_Grey tube{}row3 t*4.2 0.15 32 | xform -t {} -15 0'.format(i+1, pile1x-pilesep*i)

customObject = demo.makeCustomObject(name,text)
demo.appendtoScene(radfile=scene.radfiles, customObject=customObject, text="!xform -rz 0")

octfile = demo.makeOct()  # makeOct combines all of the ground, sky and object files we just added into a .oct file.

Custom Object Name objects\Pile.rad
Created tutorial_11.oct

View the geometry with the posts on :#

*rvu -vf views:nbsphinx-math:`front`.vp -e .01 -pe 0.4 -vp 12 -10 3.5 -vd -0.0995 0.9950 0.0 tutorial_11.oct*

[14]:

## Comment the ! line below to run rvu from the Jupyter notebook instead of your terminal. ## Simulation will stop until you close the rvu window #!rvu -vf views\front.vp -e .01 tutorial_11.oct

AgriPV modeled step 2

2. Analyse the Ground Irradiance#

Now let’s do some analysis along the ground, starting from the edge of the modules. We wil select to start in the center of the array.

We are also increasign the number of points sampled accross the collector width, with the variable sensorsy passed to moduleanalysis. We are also increasing the step between sampling points, to be able to sample in between the rows.

[16]:
analysis = AnalysisObj(octfile, demo.name)
sensorsy = 20
frontscan, backscan = analysis.moduleAnalysis(scene, sensorsy=sensorsy)

[17]:
groundscan = frontscan
[18]:
groundscan['zstart'] = 0.05  # setting it 5 cm from the ground.
groundscan['zinc'] = 0   # no tilt necessary.
groundscan['yinc'] = pitch/(sensorsy-1)   # increasing spacing so it covers all distance between rows
groundscan
[18]:
{'xstart': 1.5159739062663865e-16,
 'ystart': -1.237886635821746,
 'zstart': 0.05,
 'xinc': -1.5802346387185998e-17,
 'yinc': 0.7894736842105263,
 'zinc': 0,
 'sx_xinc': 0.0,
 'sx_yinc': 0.0,
 'sx_zinc': 0.0,
 'Nx': 1,
 'Ny': 20,
 'Nz': 1,
 'orient': '-0.000 0.574 -0.819'}
[19]:
analysis.analysis(octfile, simulationname+"_groundscan", groundscan, backscan)  # compare the back vs front irradiance

Linescan in process: tutorial_11_groundscan_Front
Linescan in process: tutorial_11_groundscan_Back
Saved: results\irr_tutorial_11_groundscan.csv
[19]:
({'Wm2': [703.8038333333334,
   673.7653666666666,
   321.8276333333333,
   315.0131666666667,
   329.5354,
   350.5611666666667,
   699.8140666666667,
   719.7485,
   727.2747333333333,
   734.5459333333333,
   742.3828,
   744.3802000000001,
   746.3888333333334,
   747.3489,
   750.8768333333334,
   751.2028666666665,
   746.8307666666666,
   741.7076999999999,
   726.5892333333333,
   706.5294333333335],
  'x': [1.515974e-16,
   1.35795e-16,
   1.199927e-16,
   1.041904e-16,
   8.838801e-17,
   7.258566e-17,
   5.678331e-17,
   4.098097e-17,
   2.517862e-17,
   9.376273e-18,
   -6.426073e-18,
   -2.222842e-17,
   -3.803077e-17,
   -5.383311e-17,
   -6.963546e-17,
   -8.543781e-17,
   -1.012402e-16,
   -1.170425e-16,
   -1.328448e-16,
   -1.486472e-16],
  'y': [-1.237887,
   -0.448413,
   0.3410607,
   1.130534,
   1.920008,
   2.709482,
   3.498955,
   4.288429,
   5.077903,
   5.867377,
   6.65685,
   7.446324,
   8.235798,
   9.025271,
   9.814745,
   10.60422,
   11.39369,
   12.18317,
   12.97264,
   13.76211],
  'z': [0.05,
   0.05,
   0.05,
   0.05,
   0.05,
   0.05,
   0.05,
   0.05,
   0.05,
   0.05,
   0.05,
   0.05,
   0.05,
   0.05,
   0.05,
   0.05,
   0.05,
   0.05,
   0.05,
   0.05],
  'r': [703.6443,
   673.6356,
   321.7167,
   314.8971,
   329.4215,
   350.4611,
   699.7151,
   719.6634,
   727.1862,
   734.4791,
   742.3076,
   744.3066,
   746.3135,
   747.2734,
   750.7906,
   751.1166,
   746.7072,
   741.5742,
   726.4468,
   706.3857],
  'g': [703.7944,
   673.7612,
   321.823,
   315.0055,
   329.5266,
   350.5535,
   699.8057,
   719.7408,
   727.2673,
   734.5402,
   742.3761,
   744.3735,
   746.3822,
   747.3423,
   750.8698,
   751.1958,
   746.8198,
   741.6976,
   726.5797,
   706.5216],
  'b': [703.9728,
   673.8993,
   321.9432,
   315.1369,
   329.6581,
   350.6689,
   699.9214,
   719.8413,
   727.3707,
   734.6185,
   742.4647,
   744.4605,
   746.4708,
   747.431,
   750.9701,
   751.2962,
   746.9653,
   741.8513,
   726.7412,
   706.681],
  'mattype': ['groundplane',
   'groundplane',
   'groundplane',
   'groundplane',
   'groundplane',
   'groundplane',
   'groundplane',
   'groundplane',
   'groundplane',
   'groundplane',
   'groundplane',
   'groundplane',
   'groundplane',
   'groundplane',
   'groundplane',
   'groundplane',
   'groundplane',
   'groundplane',
   'groundplane',
   'groundplane'],
  'title': 'tutorial_11_groundscan_Front'},
 {'Wm2': [257.0037,
   131.18226666666666,
   130.86606666666668,
   130.5496,
   124.62806666666665,
   123.07233333333333,
   257.0037,
   101.03026666666666,
   83.63291,
   92.86717666666668,
   134.92049999999998,
   127.07076666666667,
   128.4723,
   257.0037,
   131.3560666666667,
   132.55753333333334,
   133.41306666666665,
   134.14413333333334,
   135.43213333333333,
   257.0037],
  'x': [1.50052e-16,
   1.342497e-16,
   1.184474e-16,
   1.02645e-16,
   8.684266e-17,
   7.104032e-17,
   5.523797e-17,
   3.943562e-17,
   2.363328e-17,
   7.83093e-18,
   -7.971416e-18,
   -2.377376e-17,
   -3.957611e-17,
   -5.537846e-17,
   -7.11808e-17,
   -8.698315e-17,
   -1.027855e-16,
   -1.185878e-16,
   -1.343902e-16,
   -1.501925e-16],
  'y': [-1.225268,
   -1.096232,
   -0.9671961,
   -0.8381601,
   -0.7091242,
   -0.5800882,
   -0.4510523,
   -0.3220163,
   -0.1929803,
   -0.0639444,
   0.06509155,
   0.1941275,
   0.3231635,
   0.4521994,
   0.5812354,
   0.7102713,
   0.8393073,
   0.9683432,
   1.097379,
   1.226415],
  'z': [3.440837,
   3.531189,
   3.621541,
   3.711893,
   3.802245,
   3.892597,
   3.982949,
   4.073301,
   4.163653,
   4.254005,
   4.344357,
   4.434709,
   4.525061,
   4.615413,
   4.705765,
   4.796117,
   4.886468,
   4.97682,
   5.067172,
   5.157524],
  'r': [257.0037,
   130.9996,
   130.6839,
   130.3679,
   124.3862,
   122.8335,
   257.0037,
   100.267,
   82.60756,
   90.97942,
   133.8983,
   126.5879,
   128.1372,
   257.0037,
   131.153,
   132.3526,
   133.2722,
   134.0025,
   135.3221,
   257.0037],
  'g': [257.0037,
   131.1747,
   130.8585,
   130.542,
   124.6125,
   123.057,
   257.0037,
   100.9442,
   83.51275,
   92.63328,
   134.7947,
   127.0171,
   128.4373,
   257.0037,
   131.3383,
   132.5396,
   133.4021,
   134.1331,
   135.4242,
   257.0037],
  'b': [257.0037,
   131.3725,
   131.0558,
   130.7389,
   124.8855,
   123.3265,
   257.0037,
   101.8796,
   84.77842,
   94.98883,
   136.0685,
   127.6073,
   128.8424,
   257.0037,
   131.5769,
   132.7804,
   133.5649,
   134.2968,
   135.5501,
   257.0037],
  'mattype': ['sky',
   'a2.1.a5.1.0.cellPVmodule.2310',
   'a2.1.a5.2.0.cellPVmodule.2310',
   'a2.1.a5.3.0.cellPVmodule.2310',
   'a2.1.a5.4.0.cellPVmodule.2310',
   'a2.1.a5.5.0.cellPVmodule.2310',
   'sky',
   'a2.1.a5.0.1.cellPVmodule.2310',
   'a2.1.a5.1.1.cellPVmodule.2310',
   'a2.1.a5.2.1.cellPVmodule.2310',
   'a2.1.a5.3.1.cellPVmodule.2310',
   'a2.1.a5.4.1.cellPVmodule.2310',
   'a2.1.a5.5.1.cellPVmodule.2310',
   'sky',
   'a2.1.a5.0.2.cellPVmodule.2310',
   'a2.1.a5.1.2.cellPVmodule.2310',
   'a2.1.a5.2.2.cellPVmodule.2310',
   'a2.1.a5.3.2.cellPVmodule.2310',
   'a2.1.a5.4.2.cellPVmodule.2310',
   'sky'],
  'title': 'tutorial_11_groundscan_Back'})

This is the result for only one ‘chord’ accross the ground. Let’s now do a X-Y scan of the ground.

3. Analyse and MAP the Ground Irradiance#

We will use the same technique to find the irradiance on the ground used above, but will move it along the X-axis to map from the start of one module to the next.

We will sample around the module that is placed at the center of the field.

AgriPV modeled step 4

[20]:
import seaborn as sns
[22]:
sensorsx = 20
startgroundsample=-module.scenex
spacingbetweensamples = module.scenex/(sensorsx-1)

for i in range (0, sensorsx): # Will map 20 points
    frontscan, backscan = analysis.moduleAnalysis(scene, sensorsy=sensorsy)
    groundscan = frontscan
    groundscan['zstart'] = 0.05  # setting it 5 cm from the ground.
    groundscan['zinc'] = 0   # no tilt necessary.
    groundscan['yinc'] = pitch/(sensorsy-1)   # increasing spacing so it covers all distance between rows
    groundscan['xstart'] = startgroundsample + i*spacingbetweensamples   # increasing spacing so it covers all distance between rows
    analysis.analysis(octfile, simulationname+"_groundscan_"+str(i), groundscan, backscan)  # compare the back vs front irradiance

Linescan in process: tutorial_11_groundscan_0_Front
Linescan in process: tutorial_11_groundscan_0_Back
Saved: results\irr_tutorial_11_groundscan_0.csv
Linescan in process: tutorial_11_groundscan_1_Front
Linescan in process: tutorial_11_groundscan_1_Back
Saved: results\irr_tutorial_11_groundscan_1.csv
Linescan in process: tutorial_11_groundscan_2_Front
Linescan in process: tutorial_11_groundscan_2_Back
Saved: results\irr_tutorial_11_groundscan_2.csv
Linescan in process: tutorial_11_groundscan_3_Front
Linescan in process: tutorial_11_groundscan_3_Back
Saved: results\irr_tutorial_11_groundscan_3.csv
Linescan in process: tutorial_11_groundscan_4_Front
Linescan in process: tutorial_11_groundscan_4_Back
Saved: results\irr_tutorial_11_groundscan_4.csv
Linescan in process: tutorial_11_groundscan_5_Front
Linescan in process: tutorial_11_groundscan_5_Back
Saved: results\irr_tutorial_11_groundscan_5.csv
Linescan in process: tutorial_11_groundscan_6_Front
Linescan in process: tutorial_11_groundscan_6_Back
Saved: results\irr_tutorial_11_groundscan_6.csv
Linescan in process: tutorial_11_groundscan_7_Front
Linescan in process: tutorial_11_groundscan_7_Back
Saved: results\irr_tutorial_11_groundscan_7.csv
Linescan in process: tutorial_11_groundscan_8_Front
Linescan in process: tutorial_11_groundscan_8_Back
Saved: results\irr_tutorial_11_groundscan_8.csv
Linescan in process: tutorial_11_groundscan_9_Front
Linescan in process: tutorial_11_groundscan_9_Back
Saved: results\irr_tutorial_11_groundscan_9.csv
Linescan in process: tutorial_11_groundscan_10_Front
Linescan in process: tutorial_11_groundscan_10_Back
Saved: results\irr_tutorial_11_groundscan_10.csv
Linescan in process: tutorial_11_groundscan_11_Front
Linescan in process: tutorial_11_groundscan_11_Back
Saved: results\irr_tutorial_11_groundscan_11.csv
Linescan in process: tutorial_11_groundscan_12_Front
Linescan in process: tutorial_11_groundscan_12_Back
Saved: results\irr_tutorial_11_groundscan_12.csv
Linescan in process: tutorial_11_groundscan_13_Front
Linescan in process: tutorial_11_groundscan_13_Back
Saved: results\irr_tutorial_11_groundscan_13.csv
Linescan in process: tutorial_11_groundscan_14_Front
Linescan in process: tutorial_11_groundscan_14_Back
Saved: results\irr_tutorial_11_groundscan_14.csv
Linescan in process: tutorial_11_groundscan_15_Front
Linescan in process: tutorial_11_groundscan_15_Back
Saved: results\irr_tutorial_11_groundscan_15.csv
Linescan in process: tutorial_11_groundscan_16_Front
Linescan in process: tutorial_11_groundscan_16_Back
Saved: results\irr_tutorial_11_groundscan_16.csv
Linescan in process: tutorial_11_groundscan_17_Front
Linescan in process: tutorial_11_groundscan_17_Back
Saved: results\irr_tutorial_11_groundscan_17.csv
Linescan in process: tutorial_11_groundscan_18_Front
Linescan in process: tutorial_11_groundscan_18_Back
Saved: results\irr_tutorial_11_groundscan_18.csv
Linescan in process: tutorial_11_groundscan_19_Front
Linescan in process: tutorial_11_groundscan_19_Back
Saved: results\irr_tutorial_11_groundscan_19.csv

Read all the files generated into one dataframe

[12]:
filestarter = "irr_AgriPV_groundscan_"

filelist = sorted(os.listdir(os.path.join(testfolder, 'results')))
prefixed = [filename for filename in filelist if filename.startswith(filestarter)]
arrayWm2Front = []
arrayWm2Back = []
arrayMatFront = []
arrayMatBack = []
filenamed = []
faillist = []

print('{} files in the directory'.format(filelist.__len__()))
print('{} groundscan files in the directory'.format(prefixed.__len__()))
i = 0  # counter to track # files loaded.

for i in range (0, len(prefixed)):
    ind = prefixed[i].split('_')

    try:
        resultsDF = load.read1Result(os.path.join(testfolder, 'results', prefixed[i]))
        arrayWm2Front.append(list(resultsDF['Wm2Front']))
        arrayWm2Back.append(list(resultsDF['Wm2Back']))
        arrayMatFront.append(list(resultsDF['mattype']))
        arrayMatBack.append(list(resultsDF['rearMat']))
        filenamed.append(prefixed[i])
    except:
        print(" FAILED ", i, prefixed[i])
        faillist.append(prefixed[i])

resultsdf = pd.DataFrame(list(zip(arrayWm2Front, arrayWm2Back,
                                  arrayMatFront, arrayMatBack)),
                         columns = ['br_Wm2Front', 'br_Wm2Back',
                                    'br_MatFront', 'br_MatBack'])
resultsdf['filename'] = filenamed
21 files in the directory
20 groundscan files in the directory

Creating a new dataframe where each element in the front irradiance list is a column. Also transpose and reverse so it looks like a top-down view of the ground.

[13]:
df3 = pd.DataFrame(resultsdf['br_Wm2Front'].to_list())
reversed_df = df3.T.iloc[::-1]
[14]:
sns.set(rc={'figure.figsize':(11.7,8.27)})
[15]:
# Plot
ax = sns.heatmap(reversed_df)
ax.set_yticks([])
ax.set_xticks([])
ax.set_ylabel('')
ax.set_xlabel('')
print('')

../_images/tutorials_11_-_AgriPV_Systems_33_1.png

4. Adding different Albedo Sections#

Add a surface (just like we added the pillars) with a specific reflectivity to represent different albedo sections. In the image, we can see that the albedo between the crops is different than the crop albedo. Let’s assume that the abledo between the crops is higher than the crop’s albedo which wa previuosly set a 0.2.

[16]:
name='Center_Grass'
carpositionx=-2
carpositiony=-1
text='! genbox white_EPDM CenterPatch 28 12 0.1 | xform -t -14 2 0'.format(carpositionx, carpositiony)
customObject = demo.makeCustomObject(name,text)
demo.appendtoScene(scene.radfiles, customObject, '!xform -rz 0')

octfile = demo.makeOct(demo.getfilelist())


Custom Object Name objects\Center_Grass.rad
Created AgriPV.oct

Viewing with rvu:

AgriPV modeled step 4