def test_as_scene_mesh(): spc = SurfacicPointCloud(0, 0, 0, 1) sc = spc.as_scene_mesh() assert 0 in sc v, f, = sc[0] assert len(f) == 1 assert len(v) == 3
def test_data_frame(): spc = SurfacicPointCloud(0, 0, 0, 1) df = spc.as_data_frame() assert df.shape == (1, 8) spc.properties.update({'a_property': {0: 3}}) df = spc.as_data_frame() assert df.shape == (1, 9)
def test_as_triangle_scene(): spc = SurfacicPointCloud(0, 0, 0, 1) sc = spc.as_triangle_scene() assert 0 in sc triangles = sc[0] assert len(triangles) == 1 pts = triangles[0] assert len(pts) == 3 assert len(pts[0]) == 3
def test_spc_instantiation(): spc = SurfacicPointCloud(0, 0, 0, 1) for w in ('x', 'y', 'z', 'area', 'shape_id', 'normals', 'properties', 'size'): assert hasattr(spc, w) spc = SurfacicPointCloud(100, 100, 100, 10000, scene_unit='cm') assert spc.x == spc.y == spc.z == spc.area == 1 # m2 faces = (range(3), ) vertices = ((0, 0, 0), (1, 0, 0), (0, 1, 0)) sc = {'0': (vertices, faces)} spc = SurfacicPointCloud.from_scene_mesh(sc) assert spc.area == 0.5 numpy.testing.assert_array_equal(spc.normals, ((0, 0, 1), ))
def test_bbox(): faces = ((0, 1, 2), (0, 2, 0), (0, 1, 3)) vertices = ((0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)) sc = {'0': (vertices, faces)} spc = SurfacicPointCloud.from_scene_mesh(sc) expected = ((0.0, 0.0, 0.0), [1. / 3] * 3) numpy.testing.assert_almost_equal(spc.bbox(), expected)
def test_subset(): faces = ((0, 1, 2), (0, 2, 3), (0, 1, 3)) vertices = ((0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)) sc = { 1: (vertices, (faces[1], )), 2: (vertices, [faces[i] for i in (0, 2)]) } spc = SurfacicPointCloud.from_scene_mesh(sc) sub = spc.subset(point_id=1) assert len(sub.as_data_frame() == 1) sub = spc.subset(shape_id=2) assert len(sub.as_data_frame() == 2)
def test_sky_source_type(): # small Horizontal surface in big voxel spc = SurfacicPointCloud(0.5, 0.5, 0.5, area=0.1, normals=[(0, 0, 1)]) # sun_source ratp = RatpScene(spc, resolution=(1, 1, 1), rsoil=0, nbinclin=90) dfsun = ratp.do_irradiation(sun_sources=([90], [0], [1])) dfsky = ratp.do_irradiation(sky_sources=([90], [0], [1])) assert dfsky.PAR.values > dfsun.PAR.values dfscat = ratp.do_irradiation(sun_sources=([90], [0], [1]), sky_sources=([90], [0], [0]), scattering_indicatrix=[1]) numpy.testing.assert_almost_equal(dfsky.PAR.values, dfscat.PAR.values, decimal=2)
def test_inclinations(): faces = ((0, 1, 2), (0, 2, 3), (0, 1, 3)) vertices = ((0, 0, 0), (1, 0, 0), (0, 1, 0), (0, 0, 1)) sc = { 1: (vertices, (faces[1], )), 2: (vertices, [faces[i] for i in (0, 2)]) } spc = SurfacicPointCloud.from_scene_mesh(sc) df = spc.inclinations() inc = { g: x.to_dict('list')['inclination'] for g, x in df.groupby('shape_id') } numpy.testing.assert_array_equal(inc[1], [90.0]) numpy.testing.assert_array_equal(inc[2], [0.0, 90.0])
def test_serialisation(): spc = SurfacicPointCloud(0, 0, 0, 1) try: tmpdir = tempfile.mkdtemp() path = os.path.join(tmpdir, 'test.csv') spc.save(path) spc2 = SurfacicPointCloud.load(path) assert spc.x == spc2.x numpy.testing.assert_almost_equal(spc.normals, spc2.normals) spc.properties.update({'a_property': {0: 3}}) spc.save(path) spc2 = SurfacicPointCloud.load(path) assert 'a_property' in spc2.properties for k in spc.properties['a_property']: assert k in spc2.properties['a_property'] except Exception as e: raise e finally: os.remove(path) os.rmdir(tmpdir)
def test_sources(): # small Horizontal surface in big voxel spc = SurfacicPointCloud(0.5, 0.5, 0.5, area=0.1, normals=[(0, 0, 1)]) # nearly horizontal square, zenith light ratp = RatpScene(spc, resolution=(1, 1, 1), rsoil=0, nbinclin=90) dfv = ratp.do_irradiation(sun_sources=([90], [0], [1])) numpy.testing.assert_almost_equal(dfv.PAR.values, 0.96, decimal=2) # zenith light, irrad=10 dfv = ratp.do_irradiation(sun_sources=([90], [0], [10])) numpy.testing.assert_almost_equal(dfv.PAR.values, 9.66, decimal=2) # inclined light dfv = ratp.do_irradiation(sun_sources=([45], [0], [numpy.sin(numpy.radians(45))])) numpy.testing.assert_almost_equal(dfv.PAR.values, 0.69, decimal=2) # inclined + vertical light dfv = ratp.do_irradiation(sun_sources=([45, 90], [0, 0], [numpy.sin(numpy.radians(45)), 1])) numpy.testing.assert_almost_equal(dfv.PAR.values, 1.66, decimal=2) # vertical surface, zenith light vd = [0] * 89 + [1] vh = [1] + [0] * 89 dfv = ratp.do_irradiation(sun_sources=([90], [0], [1]), distinc=[vd]) numpy.testing.assert_almost_equal(dfv.PAR.values, 0.008, decimal=3) # vertical surface, near horizontal light dfv = ratp.do_irradiation(sun_sources=([1], [0], [numpy.sin(numpy.radians(1))]), distinc=[vd]) numpy.testing.assert_almost_equal(dfv.PAR.values, 0.63, decimal=2) # Test Orientation # Three filled voxels with lad heterogeneity along X+ (North/South) spc = SurfacicPointCloud([0.5, 1.5, 2.5], [0.5] * 3, [0.5] * 3, [0.1, 0.1, 100]) ratp = RatpScene(spc, resolution=(1, 1, 1), rsoil=0, rleaf=0, nbinclin=90, distinc=[vd], mu=1) # radiant source coming from X+ dfv = ratp.do_irradiation(sun_sources=([1], [0], [numpy.sin(numpy.radians(1))])) v1 = dfv.loc[dfv.jx == 1, 'PAR'].values # source coming from X- dfv = ratp.do_irradiation(sun_sources=([1], [180], [numpy.sin(numpy.radians(1))])) v2 = dfv.loc[dfv.jx == 1, 'PAR'].values # compare irrad on central voxel assert v2 > v1 # perpendicular source coming from Y- dfv = ratp.do_irradiation(sun_sources=([1], [90], [numpy.sin(numpy.radians(1))])) numpy.testing.assert_almost_equal(dfv.loc[dfv.jx == 1, 'PAR'].values, dfv.loc[dfv.jx == 0, 'PAR'].values, decimal=2) # Test left-right / East-West # a scene with high lad on Y- (east), north being towards X+ spc = SurfacicPointCloud([0.5] * 3, [0.5, 1.5, 2.5], [0.5] * 3, [100, 0.1, 0.1]) ratp = RatpScene(spc, resolution=(1, 1, 1), rsoil=0, rleaf=0, nbinclin=90, distinc=[vd], mu=1) # radiant source coming from east (north, positive clockwise convention) dfv = ratp.do_irradiation(sun_sources=([1], [90], [numpy.sin(numpy.radians(1))])) v1 = dfv.loc[dfv.jy == 1, 'PAR'].values # source coming from west dfv = ratp.do_irradiation(sun_sources=([1], [-90], [numpy.sin(numpy.radians(1))])) v2 = dfv.loc[dfv.jy == 1, 'PAR'].values assert v2 > v1 # a scene with high lad on X+ (east), north being towards Y+ spc = SurfacicPointCloud([0.5, 1.5, 2.5], [0.5] * 3, [0.5] * 3, [0.1, 0.1, 100]) ratp = RatpScene(spc, resolution=(1, 1, 1), rsoil=0, rleaf=0, nbinclin=90, distinc=[vd], mu=1, orientation=-90) # radiant source coming from east (north, positive clockwise convention) dfv = ratp.do_irradiation(sun_sources=([1], [90], [numpy.sin(numpy.radians(1))])) v1 = dfv.loc[dfv.jx == 1, 'PAR'].values # source coming from west dfv = ratp.do_irradiation(sun_sources=([1], [-90], [numpy.sin(numpy.radians(1))])) v2 = dfv.loc[dfv.jx == 1, 'PAR'].values assert v2 > v1
def __init__(self, scene=None, grid=None, entities=None, rleaf=0.15, rsoil=0.2, nbinclin=9, orientation=0, localisation='Montpellier', scene_unit='m', mu=None, distinc=None, **grid_kwds): """ Initialise a RatpScene. Arguments: scene: a pyratp.interface.surfacic_point_cloud or a plantgl.Scene or a {shape_id: vertices, faces} scene mesh dict encoding the 3D scene grid: a pyratp.interface.smart_grid instance encoding the voxel grid. If None (default), the grid is adjusted to fit the input scene. entities: a {shape_id: entity} dict defining entities in the scene. If None (default), all shapes are considered as members of the same entity rleaf: leaf reflectance or a {entity: leaf_reflectance} property dict. rsoil: soil reflectance nbinclin: the number of angular classes for leaf angle distribution orientation (float): the angle (deg, positive clockwise) from X+ to North (default: 0) localisation : a string referencing a city of the localisation database (class variable), or a dict{'longitude':longitude, 'latitude':latitude} scene_unit: a string indicating unit used for scene coordinates mu : a list of clumping indices, indexed by density.If None (default) clumping is automatically evaluated using clark-evans method. distinc: a list of list giving the frequency of the nbinclin leaf angles classes (from horizontal to vertical) per entity in the canopy. If None (default), distinc is computed automatically from the scene Other named args to this function are used for controlling grid shape when grid=None """ # scene_box will be used to construct grid, if grid is None scene_box = ((0, 0, 0), (1, 1, 1)) if scene is None: scene = {'plant': unit_square_mesh()} if isinstance(scene, SurfacicPointCloud): self.scene = scene self.scene_mesh = scene.as_scene_mesh() if grid is None: scene_box = scene.bbox() elif is_pgl_scene(scene): self.scene_mesh = pgls.as_scene_mesh(scene) self.scene = SurfacicPointCloud.from_scene_mesh( self.scene_mesh, scene_unit=scene_unit) if grid is None: scene_box = pgls.bbox(scene, scene_unit) else: try: self.scene_mesh = scene self.scene = SurfacicPointCloud.from_scene_mesh( scene, scene_unit=scene_unit) if grid is None: vertices, faces = zip(*scene.values()) vertices = reduce(lambda pts, new: pts + list(new), vertices, []) x, y, z = zip(*vertices) scene_box = ((min(x), min(y), min(z)), (max(x), max(y), max(z))) except Exception as details: print details raise ValueError( "Unrecognised scene format: should be one of pgl.Scene, " "SurfacicPointCloud or {sh_id:(vertices, faces)} dict") if grid is None: self.smart_grid = SmartGrid(scene_box=scene_box, **grid_kwds) elif isinstance(grid, SmartGrid): self.smart_grid = grid else: raise ValueError('Unrecognised grid format: should be None or a ' 'SmartGrid instance') if entities is None: entities = {sh_id: 'default' for sh_id in self.scene_mesh} self.entities = entities # RATP entity code starts at 1 ent = list(set(self.entities.values())) self.entity_map = dict(zip(ent, range(1, len(ent) + 1))) self.entity_code = numpy.array( [self.entity_map[entities[sh]] for sh in self.scene.shape_id]) if not isinstance(rleaf, dict): # if not hasattr(rleaf, '__len__'): # rleaf = [rleaf] rleaf = {'default': rleaf} self.rleaf = {self.entity_map[k]: rleaf[k] for k in self.entity_map} if not hasattr(rsoil, '__len__'): rsoil = [rsoil] self.rsoil = rsoil self.orientation = orientation if not isinstance(localisation, dict): try: localisation = RatpScene.localisation_db[localisation] except KeyError: print 'Warning : localisation', localisation, \ 'not found in database, using default localisation', \ RatpScene.localisation_db.iter().next() localisation = RatpScene.localisation_db.itervalues().next() self.localisation = localisation if mu is None: mu = self.clumping() print(' '.join(['clumping evaluated:'] + [str(m) for m in mu])) self.set_clumping(mu) if distinc is None: distinc = self.inclination_distribution(nbinclin) self.set_inclin(distinc) self.ratp_grid, self.grid_indices = self.grid()