File: gyoto_sample_standard.py

package info (click to toggle)
gyoto 2.0.2-7
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 9,444 kB
  • sloc: cpp: 42,330; sh: 4,512; python: 3,436; xml: 2,865; makefile: 691; ansic: 346
file content (190 lines) | stat: -rw-r--r-- 5,831 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
'''Sample Astrobj::Standard for using with Gyoto Python plug-in

   Those classes demonstrate how to use Python classes as Gyoto
   Astrobj::Standard implementations using Gyoto's "python"
   plug-in. Note that this plug-in can be renamed to whatever matches
   the particular version of Python it has been built against
   (e.g. python3.4).

   The goal is to be able to instantiate these from XML, from
   Yorick... and even from Python using the gyoto extension...

   Synopsis:

   import gyoto.core
   gyoto.core.requirePlugin("python") # or python2.7 or python3.4...
   sp=gyoto.core.Astrobj("Python::Standard")
   sp.set("Module", "gyoto_sample_standard")
   sp.set("Class", "FixedStar")
   sp.set("Position", (0., 0., 0.))
   sp.set("Radius", 1.)

   Classes that aim at implementing the Gyoto::Astrobj::Standard
   interface do so by providing the following methods and variable:

   __call__: required
   getVelocity: required
   giveDelta, emission, integrateEmission, transmission, set, get,
     properties: optional.
   emission and integrateEmission can be overloaded by using the
   varargs argument.

   Ad-hoc properties are declared in member properties which is a
   dict in the form:
     properties = { Key1: type1, Key2: type2}
   where Keyi and typei are strings.

   They are then handled by set(self, key, val) and get(self, key).

'''

import math
import numpy
import gyoto.core

class FixedStar:
    ''' Sample class for Astrobj::Python::Standard
    '''

    properties={"Position": "vector_double",
                "Radius": "double",
                "Spectrum": "spectrum",
                "Opacity": "spectrum"}
    '''Properties handled by set() and get()
    '''

    position=numpy.zeros(3, dtype=float)
    '''3 coordinates of the center of the star
    '''

    radius=1.
    '''Radius of the Star in geometrical units
    '''

    spectrum=gyoto.core.Spectrum("BlackBody")
    opacity=gyoto.core.Spectrum("PowerLaw")

    def set(self, key, val):
        '''Set parameters as ad hoc entities

        Python:
          instance.set("Position", (x1, x2, x3))
          instance.set("Radius", radius)

        XML:
          <Position> x1 x2 x3 </Position>
          <Radius> radius </Radius>

        Setting Radius actually sets CriticalValue and SafetyValue
        (Properties of Astrobj::Standard).

        '''
        if (key=="Position"):
            self.position = val
        elif key == "Radius":
            self.radius = val
        elif key == "Spectrum":
            self.spectrum = val
        elif key == "Opacity":
            self.opacity = val
        # Note: since we set attributes here, __setattr__(self, key,
        # val) defined below will be called and implements useful side
        # effects.

    def get(self, key):
        '''Retrieve properties

        Optional, but simple to implement and very usefull. Required
        to write back to XML, which is used for MPI parallelization.
        '''
        if key == "Position":
            return self.position
        if key == "Radius":
            return self.radius
        if key == "Spectrum":
            return self.spectrum
        if key == "Opacity":
            return self.opacity

    def __setattr__(self, key, val):
        '''Set attributes

        Optional, but allows reacting when attributes (self.xxx)
        change. In particular, Gyoto will set self.this to the C++
        class instance upon initialization.
        '''
        # First, actually store the attribute. This is what would
        # happen if we did not overload __setattr__.
        self.__dict__[key]=val
        if key == "this":
            # Then, if key is "this", trigger the side effects of
            # setting position and radius. This will reenter
            # __setattr__ with the following cases.
            self.position=self.position
            self.radius=self.radius
        elif key == "position":
            # when position is set, cache Cartesian expression
            pos=numpy.zeros(4, dtype=float)
            pos[1:]=val
            self.coord_st=self.to_cartesian(pos)
        elif key == "radius":
            # Update two properties from the Gyoto::Standard interface
            self.this.set("CriticalValue", val**2)
            self.this.set("SafetyValue", val**2*1.1+0.1)

    def to_cartesian(self, coord):
        '''Helper function, not in the API

        '''
        gg=self.this.metric()
        spherical=False
        if gg is not None:
            spherical = gg.coordKind() == gyoto.core.GYOTO_COORDKIND_SPHERICAL
        if spherical:
            rs=coord[1]
            ths=coord[2]
            phs=coord[3]
            st=math.sin(ths)
            ct=math.cos(ths)
            sp=math.sin(phs)
            cp=math.cos(phs)
            return numpy.array((coord[0], rs*st*cp, rs*st*sp, rs*ct))
        return coord

    def __call__(self, coord):
        ''' Astrobj::Standard::operator()()

        Required
        '''
        coord_ph=self.to_cartesian(coord)
        coord_st=self.coord_st
        dx = coord_ph[1]-coord_st[1]
        dy = coord_ph[2]-coord_st[2]
        dz = coord_ph[3]-coord_st[3]
        return dx*dx + dy*dy + dz*dz

    def getVelocity(self, coord, vel):
        ''' Velocity field

        Required
        '''
        vel[0]=1.
        for i in range(1, 4):
            vel[i]=0.

    def emission(self, nuem, dsem, cph, co):
        ''' emission

        Optional
        '''
        if self.this.opticallyThin():
            return self.spectrum(nuem, self.opacity(nuem), dsem)
        return self.spectrum(nuem)

    def transmission(self, nuem, dsem, cph, co):
        if (not self.this.opticallyThin()):
            return 0.
        opac=self.opacity(nuem)
        if opac == 0.:
            return 1.
        return math.exp(-opac*dsem)