PyMPDATA.solver
class grouping user-supplied stepper, fields and post-step/post-iter hooks, as well as self-initialised temporary storage
1""" 2class grouping user-supplied stepper, fields and post-step/post-iter hooks, 3as well as self-initialised temporary storage 4""" 5 6from typing import Union 7 8import numba 9 10from .impl.meta import META_IS_NULL 11from .scalar_field import ScalarField 12from .stepper import Stepper 13from .vector_field import VectorField 14 15 16@numba.experimental.jitclass([]) 17class PostStepNull: # pylint: disable=too-few-public-methods 18 """do-nothing version of the post-step hook""" 19 20 def __init__(self): 21 pass 22 23 def call(self, psi, step): # pylint: disable-next=unused-argument 24 """think of it as a `__call__` method (which Numba does not allow)""" 25 26 27@numba.experimental.jitclass([]) 28class PostIterNull: # pylint: disable=too-few-public-methods 29 """do-nothing version of the post-iter hook""" 30 31 def __init__(self): 32 pass 33 34 def call(self, flux, g_factor, step, iteration): # pylint: disable=unused-argument 35 """think of it as a `__call__` method (which Numba does not allow)""" 36 37 38class Solver: 39 """Solution orchestrator requiring prior instantiation of: a `Stepper`, 40 a scalar advectee field (that which is advected), 41 a vector advector field (that which advects), 42 and optionally a scalar g_factor field (used in some cases of the advection equation). 43 Note: in some cases of advection, i.e. momentum advection, 44 the advectee can act upon the advector. 45 See `PyMPDATA_examples.Jarecka_et_al_2015` for an example of this. 46 """ 47 48 def __init__( 49 self, 50 stepper: Stepper, 51 advectee: ScalarField, 52 advector: VectorField, 53 g_factor: [ScalarField, None] = None, 54 ): 55 def scalar_field(dtype=None): 56 return ScalarField.clone(advectee, dtype=dtype) 57 58 def null_scalar_field(): 59 return ScalarField.make_null(advectee.n_dims, stepper.traversals) 60 61 def vector_field(): 62 return VectorField.clone(advector) 63 64 def null_vector_field(): 65 return VectorField.make_null(advector.n_dims, stepper.traversals) 66 67 for field in [advector, advectee] + ( 68 [g_factor] if g_factor is not None else [] 69 ): 70 assert field.halo == stepper.options.n_halo 71 assert field.dtype == stepper.options.dtype 72 assert field.grid == advector.grid 73 74 self.__fields = { 75 "advectee": advectee, 76 "advector": advector, 77 "g_factor": g_factor or null_scalar_field(), 78 "vectmp_a": vector_field(), 79 "vectmp_b": vector_field(), 80 "vectmp_c": ( 81 vector_field() 82 if stepper.options.non_zero_mu_coeff 83 else null_vector_field() 84 ), 85 "nonosc_xtrm": ( 86 scalar_field(dtype=complex) 87 if stepper.options.nonoscillatory 88 else null_scalar_field() 89 ), 90 "nonosc_beta": ( 91 scalar_field(dtype=complex) 92 if stepper.options.nonoscillatory 93 else null_scalar_field() 94 ), 95 } 96 for field in self.__fields.values(): 97 field.assemble(stepper.traversals) 98 99 self.__stepper = stepper 100 101 @property 102 def advectee(self) -> ScalarField: 103 """advectee scalar field (with halo), modified by advance(), 104 may be modified from user code (e.g., source-term handling)""" 105 return self.__fields["advectee"] 106 107 @property 108 def advector(self) -> VectorField: 109 """advector vector field (with halo), unmodified by advance(), 110 may be modified from user code""" 111 return self.__fields["advector"] 112 113 @property 114 def g_factor(self) -> ScalarField: 115 """G_factor field (with halo), unmodified by advance(), assumed to be constant-in-time. 116 Can be used as a Jacobian for coordinate transformations, 117 e.g. into spherical coordinates. 118 For this type of usage, see 119 `PyMPDATA_examples.Williamson_and_Rasch_1989_as_in_Jaruga_et_al_2015_Fig_14`. 120 Can also be used to account for spatial variability of fluid density, see 121 `PyMPDATA_examples.Shipway_and_Hill_2012`. 122 e.g. the changing density of a fluid.""" 123 return self.__fields["g_factor"] 124 125 def advance( 126 self, 127 n_steps: int, 128 mu_coeff: Union[tuple, None] = None, 129 post_step=None, 130 post_iter=None, 131 ): 132 """advances solution by `n_steps` steps, optionally accepts: a tuple of diffusion 133 coefficients (one value per dimension) as well as `post_iter` and `post_step` 134 callbacks expected to be `numba.jitclass`es with a `call` method, for 135 signature see `PostStepNull` and `PostIterNull`; 136 returns CPU time per timestep (units as returned by `clock.clock()`)""" 137 if mu_coeff is not None: 138 assert self.__stepper.options.non_zero_mu_coeff 139 else: 140 mu_coeff = (0.0, 0.0, 0.0) 141 if ( 142 self.__stepper.options.non_zero_mu_coeff 143 and not self.__fields["g_factor"].meta[META_IS_NULL] 144 ): 145 raise NotImplementedError() 146 147 post_step = post_step or PostStepNull() 148 post_iter = post_iter or PostIterNull() 149 150 return self.__stepper( 151 n_steps=n_steps, 152 mu_coeff=mu_coeff, 153 post_step=post_step, 154 post_iter=post_iter, 155 fields=self.__fields, 156 )
39class Solver: 40 """Solution orchestrator requiring prior instantiation of: a `Stepper`, 41 a scalar advectee field (that which is advected), 42 a vector advector field (that which advects), 43 and optionally a scalar g_factor field (used in some cases of the advection equation). 44 Note: in some cases of advection, i.e. momentum advection, 45 the advectee can act upon the advector. 46 See `PyMPDATA_examples.Jarecka_et_al_2015` for an example of this. 47 """ 48 49 def __init__( 50 self, 51 stepper: Stepper, 52 advectee: ScalarField, 53 advector: VectorField, 54 g_factor: [ScalarField, None] = None, 55 ): 56 def scalar_field(dtype=None): 57 return ScalarField.clone(advectee, dtype=dtype) 58 59 def null_scalar_field(): 60 return ScalarField.make_null(advectee.n_dims, stepper.traversals) 61 62 def vector_field(): 63 return VectorField.clone(advector) 64 65 def null_vector_field(): 66 return VectorField.make_null(advector.n_dims, stepper.traversals) 67 68 for field in [advector, advectee] + ( 69 [g_factor] if g_factor is not None else [] 70 ): 71 assert field.halo == stepper.options.n_halo 72 assert field.dtype == stepper.options.dtype 73 assert field.grid == advector.grid 74 75 self.__fields = { 76 "advectee": advectee, 77 "advector": advector, 78 "g_factor": g_factor or null_scalar_field(), 79 "vectmp_a": vector_field(), 80 "vectmp_b": vector_field(), 81 "vectmp_c": ( 82 vector_field() 83 if stepper.options.non_zero_mu_coeff 84 else null_vector_field() 85 ), 86 "nonosc_xtrm": ( 87 scalar_field(dtype=complex) 88 if stepper.options.nonoscillatory 89 else null_scalar_field() 90 ), 91 "nonosc_beta": ( 92 scalar_field(dtype=complex) 93 if stepper.options.nonoscillatory 94 else null_scalar_field() 95 ), 96 } 97 for field in self.__fields.values(): 98 field.assemble(stepper.traversals) 99 100 self.__stepper = stepper 101 102 @property 103 def advectee(self) -> ScalarField: 104 """advectee scalar field (with halo), modified by advance(), 105 may be modified from user code (e.g., source-term handling)""" 106 return self.__fields["advectee"] 107 108 @property 109 def advector(self) -> VectorField: 110 """advector vector field (with halo), unmodified by advance(), 111 may be modified from user code""" 112 return self.__fields["advector"] 113 114 @property 115 def g_factor(self) -> ScalarField: 116 """G_factor field (with halo), unmodified by advance(), assumed to be constant-in-time. 117 Can be used as a Jacobian for coordinate transformations, 118 e.g. into spherical coordinates. 119 For this type of usage, see 120 `PyMPDATA_examples.Williamson_and_Rasch_1989_as_in_Jaruga_et_al_2015_Fig_14`. 121 Can also be used to account for spatial variability of fluid density, see 122 `PyMPDATA_examples.Shipway_and_Hill_2012`. 123 e.g. the changing density of a fluid.""" 124 return self.__fields["g_factor"] 125 126 def advance( 127 self, 128 n_steps: int, 129 mu_coeff: Union[tuple, None] = None, 130 post_step=None, 131 post_iter=None, 132 ): 133 """advances solution by `n_steps` steps, optionally accepts: a tuple of diffusion 134 coefficients (one value per dimension) as well as `post_iter` and `post_step` 135 callbacks expected to be `numba.jitclass`es with a `call` method, for 136 signature see `PostStepNull` and `PostIterNull`; 137 returns CPU time per timestep (units as returned by `clock.clock()`)""" 138 if mu_coeff is not None: 139 assert self.__stepper.options.non_zero_mu_coeff 140 else: 141 mu_coeff = (0.0, 0.0, 0.0) 142 if ( 143 self.__stepper.options.non_zero_mu_coeff 144 and not self.__fields["g_factor"].meta[META_IS_NULL] 145 ): 146 raise NotImplementedError() 147 148 post_step = post_step or PostStepNull() 149 post_iter = post_iter or PostIterNull() 150 151 return self.__stepper( 152 n_steps=n_steps, 153 mu_coeff=mu_coeff, 154 post_step=post_step, 155 post_iter=post_iter, 156 fields=self.__fields, 157 )
Solution orchestrator requiring prior instantiation of: a Stepper
,
a scalar advectee field (that which is advected),
a vector advector field (that which advects),
and optionally a scalar g_factor field (used in some cases of the advection equation).
Note: in some cases of advection, i.e. momentum advection,
the advectee can act upon the advector.
See PyMPDATA_examples.Jarecka_et_al_2015
for an example of this.
49 def __init__( 50 self, 51 stepper: Stepper, 52 advectee: ScalarField, 53 advector: VectorField, 54 g_factor: [ScalarField, None] = None, 55 ): 56 def scalar_field(dtype=None): 57 return ScalarField.clone(advectee, dtype=dtype) 58 59 def null_scalar_field(): 60 return ScalarField.make_null(advectee.n_dims, stepper.traversals) 61 62 def vector_field(): 63 return VectorField.clone(advector) 64 65 def null_vector_field(): 66 return VectorField.make_null(advector.n_dims, stepper.traversals) 67 68 for field in [advector, advectee] + ( 69 [g_factor] if g_factor is not None else [] 70 ): 71 assert field.halo == stepper.options.n_halo 72 assert field.dtype == stepper.options.dtype 73 assert field.grid == advector.grid 74 75 self.__fields = { 76 "advectee": advectee, 77 "advector": advector, 78 "g_factor": g_factor or null_scalar_field(), 79 "vectmp_a": vector_field(), 80 "vectmp_b": vector_field(), 81 "vectmp_c": ( 82 vector_field() 83 if stepper.options.non_zero_mu_coeff 84 else null_vector_field() 85 ), 86 "nonosc_xtrm": ( 87 scalar_field(dtype=complex) 88 if stepper.options.nonoscillatory 89 else null_scalar_field() 90 ), 91 "nonosc_beta": ( 92 scalar_field(dtype=complex) 93 if stepper.options.nonoscillatory 94 else null_scalar_field() 95 ), 96 } 97 for field in self.__fields.values(): 98 field.assemble(stepper.traversals) 99 100 self.__stepper = stepper
102 @property 103 def advectee(self) -> ScalarField: 104 """advectee scalar field (with halo), modified by advance(), 105 may be modified from user code (e.g., source-term handling)""" 106 return self.__fields["advectee"]
advectee scalar field (with halo), modified by advance(), may be modified from user code (e.g., source-term handling)
108 @property 109 def advector(self) -> VectorField: 110 """advector vector field (with halo), unmodified by advance(), 111 may be modified from user code""" 112 return self.__fields["advector"]
advector vector field (with halo), unmodified by advance(), may be modified from user code
114 @property 115 def g_factor(self) -> ScalarField: 116 """G_factor field (with halo), unmodified by advance(), assumed to be constant-in-time. 117 Can be used as a Jacobian for coordinate transformations, 118 e.g. into spherical coordinates. 119 For this type of usage, see 120 `PyMPDATA_examples.Williamson_and_Rasch_1989_as_in_Jaruga_et_al_2015_Fig_14`. 121 Can also be used to account for spatial variability of fluid density, see 122 `PyMPDATA_examples.Shipway_and_Hill_2012`. 123 e.g. the changing density of a fluid.""" 124 return self.__fields["g_factor"]
G_factor field (with halo), unmodified by advance(), assumed to be constant-in-time.
Can be used as a Jacobian for coordinate transformations,
e.g. into spherical coordinates.
For this type of usage, see
PyMPDATA_examples.Williamson_and_Rasch_1989_as_in_Jaruga_et_al_2015_Fig_14
.
Can also be used to account for spatial variability of fluid density, see
PyMPDATA_examples.Shipway_and_Hill_2012
.
e.g. the changing density of a fluid.
126 def advance( 127 self, 128 n_steps: int, 129 mu_coeff: Union[tuple, None] = None, 130 post_step=None, 131 post_iter=None, 132 ): 133 """advances solution by `n_steps` steps, optionally accepts: a tuple of diffusion 134 coefficients (one value per dimension) as well as `post_iter` and `post_step` 135 callbacks expected to be `numba.jitclass`es with a `call` method, for 136 signature see `PostStepNull` and `PostIterNull`; 137 returns CPU time per timestep (units as returned by `clock.clock()`)""" 138 if mu_coeff is not None: 139 assert self.__stepper.options.non_zero_mu_coeff 140 else: 141 mu_coeff = (0.0, 0.0, 0.0) 142 if ( 143 self.__stepper.options.non_zero_mu_coeff 144 and not self.__fields["g_factor"].meta[META_IS_NULL] 145 ): 146 raise NotImplementedError() 147 148 post_step = post_step or PostStepNull() 149 post_iter = post_iter or PostIterNull() 150 151 return self.__stepper( 152 n_steps=n_steps, 153 mu_coeff=mu_coeff, 154 post_step=post_step, 155 post_iter=post_iter, 156 fields=self.__fields, 157 )
advances solution by n_steps
steps, optionally accepts: a tuple of diffusion
coefficients (one value per dimension) as well as post_iter
and post_step
callbacks expected to be numba.jitclass
es with a call
method, for
signature see PostStepNull
and PostIterNull
;
returns CPU time per timestep (units as returned by clock.clock()
)