solprop.backends.numpy_ref.general_modal#
The general modal guide’s nonlinear source, on the reference backend.
This is a second execution path beside solprop.backends.numpy_ref.passes, not
an extension of it: the legacy pipeline synthesises with one real static matrix in the
time domain, and a general guide’s synthesis is a complex, frequency-dependent map of
three Cartesian components, so it happens in the frequency domain instead.
The pipeline of one stage, for a chunk of quadrature nodes:
Scale. Multiply the coarse channel spectra by the pad scale
n_to / n_t, which keeps the two time-domain signals equal at coincident samples (conventions.mdsection 3), and by the analytic-signal doubling. Both are applied here and not folded intoS, becauseSis the declared physical profile in V/(m sqrt(W)) that the guide’s provider declares.Synthesise.
E(q, c, w) = sum_m S(q, c, m, w) a(m, w)at the chunk’s nodes, on the coarse grid the state lives on, placed into the fine spectrum atgrids.crop_index; then one complex inverse transform per node and component of that doubled one-sided spectrum, which is the analytic signalE_aofconventions.mdsection 4 exactly:E(t) = Re E_a(t)and|E_a|^2is the instantaneous power. A complex profile can only act on an analytic signal. Synthesising before the padding rather than after it is the same arithmetic – every fine bin outsidecrop_indexmultiplies a zero amplitude – over a mapn_omega_fine / n_omegatimes smaller.Respond.
P_i = eps0 sum_jkl A(q)_ijkl E_j E_k conj(E_l)per node, withA(q)the sum ofchi3 Tover the responses of that node’s material region (solprop.translate.responses.isotropic_kerr_tensor()). A node whose region names no response has no polarisation. Which fieldEis here is the record’s own declaration, read back from itsmodel_id: the real fieldRe E_afor the real cubicP = eps0 chi3 (E.E) E, which is the legacy field route’s model and radiates the third harmonic, or the analytic signal itself forP = eps0 chi3 (2 |E|^2 E + (E.E) E*) / 4, which carries the fundamental band alone. The tensor carries the normalisation that belongs to each. The analytic form is contracted densely, over the pair historyE_k conj(E_l)and thenj. The real cubic is symmetric in(j, k, l), so it is evaluated as the symmetric contraction (symmetric_cubic()): ten monomials(E_j E_k) E_lwithj <= k <= lper sample, against coefficients that sum each node’s tensor over each monomial’s orderings (symmetric_kerr_coefficients()), formed once per node and evaluation. That is exact for any tensor, and it is the reduction order every backend runs.Project. The physical polarisation is
Re P_a, so its one-sided spectrum – the same real forward transform every other route takes – is what drives the one-sided state. Contract it withQon the coarse grid and multiply by the record’ssource_frequency_factor,-i omega.
The crop scale n_t / n_to stays in responses.prefactor, where it is on every
route, and the spectral collar
and the interaction picture are applied by the shared
solprop.backends.numpy_ref.passes.stage_output().
The delayed response. A material region may declare the silica tensor Raman model
beside its Kerr response (solprop.nonlinear.raman.SilicaTensorResponse), and
step 3 then adds its polarisation: six real symmetric pair histories
Q_kl = E_k E_l per node, convolved on a doubled axis with that response’s trace
and rank-two kernels, and contracted with chi3 D[term] and the local field. The
instantaneous tensor already carries the 1 - f_R that leaves, so the two shares
add to the region’s one chi3.
What this route does not do. The analytic-signal form radiates no third harmonic, which is why it is not the route’s default: it remains selectable, and is the tested oracle for the fundamental band that the real cubic is measured against.
Memory in the real form. The chunk buffers of step 3 are float64 rather than
complex128 when the field is real, and the symmetric contraction holds six pair
products, a monomial and two component-wide sums per node and sample in place of the
pair history and the contracted tensor (the pair history too when a delayed response
needs its drive): at most 22 doubles against the 36 that general_memory()’s
general_node_pair and general_node_weighted rows count for three components,
so those rows are an upper bound in that form. The bound is the one a budget needs and
the ledger is not split by form. The symmetric coefficients are formed per chunk from
the tensor, n_components * n_monomials doubles a node, a transient like the
others the ledger leaves out.
Module Attributes
Delayed channels the silica tensor Raman model carries: trace and rank two. |
|
Independent entries of the real symmetric pair history of three components. |
|
Chunk buffers the general source holds per node, each |
|
Chunk buffers the general source holds per node, each |
Functions
|
Return one chunk's delayed polarisation, in C/m^2, from its pair history. |
|
Return one chunk's dispersive polarisation, in C/m^2, region by region. |
|
Return the full symmetric pair history from its six independent entries. |
Return the buffers per node and component SEP 6's response adds to a chunk. |
|
|
Return the bytes the general source holds, one row per named buffer. |
|
Return the nodes the general source may hold at once within a byte budget. |
|
Evaluate one stage's modal source through the general guide's maps. |
|
Derive the delayed response's per-node ownership, tensors and kernel spectra. |
|
Return which cubic a compiled per-region record declares. |
|
Sum each node's material responses into one susceptibility tensor. |
|
Refuse a per-region response this route does not execute, before it allocates. |
|
Return the real cubic |
|
Sum a per-node tensor over the orderings of each monomial of the real cubic. |
|
Return the real cubic's monomials |
Classes
|
The arrays one general modal problem's source pass reads, at one position. |
- solprop.backends.numpy_ref.general_modal.BUFFERS_PER_GENERAL_NODE = 4#
Chunk buffers the general source holds per node, each
n_componentswide.The synthesised fine spectrum, the analytic time field, the polarisation in time and its one-sided spectrum.
- solprop.backends.numpy_ref.general_modal.DELAYED_TERMS = 2#
Delayed channels the silica tensor Raman model carries: trace and rank two.
- class solprop.backends.numpy_ref.general_modal.GeneralMaps(synthesis, projection, tensor, frequency_factor, node_chunk, form='analytic', node_response=None, delayed_tensor=None, delayed_spectrum=None, synthesis_terms=None, projection_terms=None, dispersive=None, node_region=None)[source]#
Bases:
objectThe arrays one general modal problem’s source pass reads, at one position.
Derived once at construction from the problem’s leaves. The route propagates constant geometry, so the maps have one interval of one Chebyshev degree along z and there is nothing to evaluate per stage.
- Attributes:
- synthesisnumpy.ndarray
Son the coarse grid, complex128, shape(n_nodes, n_components, n_modes, n_omega), in V/(m sqrt(W)).- projectionnumpy.ndarray
Qon the coarse grid, complex128, shape(n_modes, n_nodes, n_components, n_omega), in V m/sqrt(W).- tensornumpy.ndarray
The summed
chi3 Tof each node’s material region, float64, shape(n_nodes, n_components, n_components, n_components, n_components), in m²/V². Zero at a node whose region does not respond.- frequency_factornumpy.ndarray
-i omega, complex128, shape(n_omega,), in rad/s.- node_chunkint
Nodes the pass handles at a time, at least one.
- node_responsenumpy.ndarray
Which response owns each node, int32, shape
(n_nodes,),-1where a node’s region declares none. It is what lets the delayed convolution run once per response over that response’s own nodes, with its own kernel.- delayed_tensornumpy.ndarray
chi3_r D[term]of each response and channel, float64, shape(n_responses, n_terms, n_components, n_components, n_components, n_components), in m²/V². Empty when no region declares a delayed response.- delayed_spectrumnumpy.ndarray
The real transform of each kernel on the doubled fine axis, times the temporal weight, complex128, shape
(n_responses, n_terms, n_to + 1), in s/s. Empty with delayed_tensor.- form{‘analytic’, ‘real’}
Which field the tensor is contracted against: the analytic signal, whose polarisation carries the fundamental band alone, or the real three-component field, whose cubic carries the third harmonic. Read from the record’s
model_idbynode_kerr_form(); tensor already carries the normalisation that goes with it.- dispersivesolprop.translate.responses.DispersiveKerrTerms or None
The gas regions’ dispersive channels (SEP 6), each acting at the nodes of its own region, or
None.- node_regionnumpy.ndarray or None
Each node’s material region, int32, shape
(n_nodes,): where each dispersive channel acts. Present with dispersive.
- at(weights)[source]#
Return the maps a stage evaluates at, from its
transform_weights.A constant guide’s maps are returned as they are, whatever weights says, which keeps its arithmetic bit for bit what it was. A moving guide’s are the weighted sums of the stored coefficients, exactly as
dPhiis the weighted sum of its terms (docs/dev/kernel-contract.mdsection 1).- Parameters:
- weightsnumpy.ndarray or None
The stage bundle’s
transform_weights, float64, one per term;Nonefor maps that do not move.
- Returns:
- GeneralMaps
The maps at the stage’s position.
- Raises:
- BackendError
If moving maps are asked for with no weights, or constant ones with some.
- delayed_spectrum: Complex[ndarray, 'n_responses_spectrum n_terms_spectrum n_rfft_spectrum'] | None = None#
- delayed_tensor: Float[ndarray, 'n_responses_delayed n_terms_delayed n_components_delayed_i n_components_delayed_j n_components_delayed_k n_components_delayed_l'] | None = None#
- dispersive: DispersiveKerrTerms | None = None#
- frequency_factor: Complex[ndarray, 'n_omega_factor']#
- property moving: bool[source]#
Return whether the maps move along z.
- Returns:
- bool
True when the synthesis and projection are Chebyshev terms a stage combines over its
transform_weights.
- property n_modes: int[source]#
Return how many channels the guide propagates.
- Returns:
- int
The channel axis of
synthesis.
- property n_nodes: int[source]#
Return how many quadrature nodes the guide’s rule carries.
- Returns:
- int
The node axis of
synthesis.
- property n_terms: int[source]#
Return how many delayed channels the maps carry.
- Returns:
- int
Zero when no region declares a delayed response.
- projection: Complex[ndarray, 'n_modes_q n_nodes_q n_components_q n_omega_q']#
- projection_terms: Complex[ndarray, 'n_map_terms_q n_modes_mq n_nodes_mq n_components_mq n_omega_mq'] | None = None#
- synthesis: Complex[ndarray, 'n_nodes_s n_components_s n_modes_s n_omega_s']#
- synthesis_terms: Complex[ndarray, 'n_map_terms_s n_nodes_ms n_components_ms n_modes_ms n_omega_ms'] | None = None#
- tensor: Float[ndarray, 'n_nodes_t n_components_t_i n_components_t_j n_components_t_k n_components_t_l']#
- solprop.backends.numpy_ref.general_modal.PAIR_BUFFERS_PER_GENERAL_NODE = 2#
Chunk buffers the general source holds per node, each
n_componentssquared.The tensor contraction is written in two steps rather than as one four-operand
einsum, so that every intermediate it allocates is a buffer this module names: the pair historyE_k conj(E_l)and the tensor contracted against it. A singleeinsumwithoptimize=Trueallocates the same intermediates and names none of them, which a memory ledger cannot report.The static maps
SandQ, the per-node tensor, the dense E/H reference, its reciprocal partners and the coefficient dual are whole-problem allocations, counted row by row bygeneral_memory(); they do not shrink with the chunk, which is why the counts here andsolprop.translate.modal.BUFFERS_PER_NODEare different and neither reuses the other.
- solprop.backends.numpy_ref.general_modal.PAIR_HISTORIES = 6#
Independent entries of the real symmetric pair history of three components.
Q_kl = E_k E_lis symmetric, soxx, yy, zz, xy, xz, yzcarry all of it and the convolution – the expensive part of a delayed response – runs six times per node and not nine.
- solprop.backends.numpy_ref.general_modal.delayed_polarisation(field, pair, maps, owner)[source]#
Return one chunk’s delayed polarisation, in C/m^2, from its pair history.
Six real symmetric histories per node, convolved with each response’s own kernel on the doubled fine axis – so the tail of a hundred-femtosecond response cannot wrap onto the head of the drive – and contracted with that response’s
chi3 D[term]and the local field.The loop is over responses rather than nodes because a convolution is not linear in its kernel’s owner: two material regions may declare different delayed models, and each one’s nodes are convolved with its own kernel.
- Parameters:
- fieldnumpy.ndarray
The local field at the chunk’s nodes, shape
(n_nodes, 3, n_to), V/m: the real field for the real cubic and the analytic signal otherwise.- pairnumpy.ndarray
The real drive
Q_kl, shape(n_nodes, 3, 3, n_to), V²/m².- mapsGeneralMaps
The problem’s maps, carrying the delayed tensors and kernel spectra.
- ownernumpy.ndarray
Which response owns each node of the chunk,
-1for none.
- Returns:
- numpy.ndarray
The delayed polarisation, shape
(n_nodes, 3, n_to), C/m².
- solprop.backends.numpy_ref.general_modal.dispersive_polarisation(field, terms, node_region, densities)[source]#
Return one chunk’s dispersive polarisation, in C/m^2, region by region.
Each channel of SEP 6’s dispersive cubic – one species of one region’s gas fill – acts at the nodes of its own region, on the real three-component field:
P = coefficient rho cubic(E)for the polynomial, and for the symmetric evaluator one cubic per region, under its gas fill’s mixture filter (evaluations()), bysolprop.nonlinear.responses.dispersive_cubic(), the arithmetic of the referenceevaluate(), whose isotropic Kleinman contraction is the real cubic’s tensor (SEP 6, decision Q3). The sector is the leaf’s: the full cubic besidekerr_form="real", the number-conserving one beside"analytic", and both are real polarisations, which the projection takes as it takesRe P_a.- Parameters:
- fieldnumpy.ndarray
The real field at the chunk’s nodes,
Re E_a, shape(n_nodes, n_components, n_to), in V/m.- termssolprop.translate.responses.DispersiveKerrTerms
The compiled channels, carrying their node regions.
- node_regionnumpy.ndarray
Each node’s material region.
- densitiessequence of float
Each channel’s number density, m^-3.
- Returns:
- numpy.ndarray
The polarisation, shape
(n_nodes, n_components, n_to), C/m^2; zero at a node whose region has no channel.
- solprop.backends.numpy_ref.general_modal.expand_pair_history(stored)[source]#
Return the full symmetric pair history from its six independent entries.
- Parameters:
- storednumpy.ndarray
The six entries
xx, yy, zz, xy, xz, yz, shape(n_nodes, 6, n_to), in V²/m².
- Returns:
- numpy.ndarray
The symmetric
(n_nodes, 3, 3, n_to)history, in V²/m².
- solprop.backends.numpy_ref.general_modal.general_dispersive_buffers(leaf)[source]#
Return the buffers per node and component SEP 6’s response adds to a chunk.
The compiled general workspace holds, for each node of a chunk and each of its components, every filtered field’s spectrum and time series and every product and its spectrum: two complex-equivalent buffers per field and two per product. The symmetric evaluator has one field and one product per region with a gas fill (a mixture’s under its mixture filter); the polynomial two fields and two products per group of equal leg bound, and one cube per region (
groups()).- Parameters:
- leafsolprop.translate.responses.DispersiveKerrTerms or None
The route’s dispersive channels, or
None.
- Returns:
- int
Complex-equivalent buffers per node and component; zero without the leaf.
- solprop.backends.numpy_ref.general_modal.general_memory(maps, tables, n_to)[source]#
Return the bytes the general source holds, one row per named buffer.
Three groups. The execution maps
SandQand the per-node tensor are derived once. The reference the record carries – the dense E and H, their reciprocal partners and the coefficient dual – is resident for the whole run because the problem owns it, and is counted here although this pass reads none of it: it is on the far side of the seam, it is the largest group on a guide with many channels, and a budget that did not see it would be wrong by that much. The chunk buffers are the pass’s own, and shrink withnode_chunk.Per-stage transients are not counted: the doubling weights, the inverse transform’s output beside the zero-filled buffer it replaces, the conjugated field, the contiguous copy
rfftmakes of a strided real view and its fine one-sided output before the crop. Measured withtracemallocon a 288-node strand, they add 6.2 % to the rows below at their peak (2.52 MiB over 40.9 MiB); each is freed inside the pass.- Parameters:
- mapsGeneralMaps
The derived maps of one problem.
- tablessolprop.translate.modal_tables.ModalTransformTables
The record the maps came from, whose stored reference is resident.
- n_toint
Fine time samples of the run’s grid, positive.
- Returns:
- dict
Byte counts keyed by buffer name, each counted once:
general_synthesis,general_projection– every coefficient of a moving guide’s maps –general_stage_maps, the two maps a moving guide’s stage evaluates (zero otherwise), andgeneral_tensor; the resident referencegeneral_electric,general_magnetic,general_backward_electric,general_backward_magnetic,general_reciprocal_gramandgeneral_coordinate_dual, zero where the record leaves one empty; and the chunk buffersgeneral_scaled_state,general_node_spectrum,general_node_field,general_node_pair,general_node_weighted,general_node_polarisationandgeneral_node_source.
- solprop.backends.numpy_ref.general_modal.general_node_chunk(n_components, n_to, n_omega_fine, budget, *, dispersive=0)[source]#
Return the nodes the general source may hold at once within a byte budget.
The chunk bounds the space-domain work alone:
BUFFERS_PER_GENERAL_NODEcomplex buffers per node and component,PAIR_BUFFERS_PER_GENERAL_NODEper node and component squared, and SEP 6’s dispersive response’s own (general_dispersive_buffers()), over the longer of the fine time axis and the fine spectrum. The maps, the reference and the dual are whole-problem allocations that no chunk shrinks (general_memory()).- Parameters:
- n_componentsint
Cartesian components of the local field, positive.
- n_toint
Fine time samples, positive.
- n_omega_fineint
Fine spectral bins, positive.
- budgetint
Bytes the chunk buffers may occupy in total, positive.
- dispersiveint, optional
The dispersive response’s complex buffers per node and component; zero, the default, without one.
- Returns:
- int
The chunk size, at least one.
- Raises:
- BackendError
If the budget is not positive.
Examples
>>> from solprop.backends.numpy_ref.general_modal import general_node_chunk >>> general_node_chunk(3, 1024, 513, 64 * 2**20) 136
- solprop.backends.numpy_ref.general_modal.general_source(state, maps, crop_index, *, n_to, pad_scale, transform_weights=None, dispersive_densities=())[source]#
Evaluate one stage’s modal source through the general guide’s maps.
The four steps of the module docstring, in the cubic
maps.formdeclares, with the gas regions’ dispersive channels added at step 3 (dispersive_polarisation()). The result is the projected polarisation times-i omega:Qis in V m/sqrt(W) andPin C/m^2, so the product carriesC V m^-1 W^(-1/2) s^-1, andC Vis a joule – the unit is sqrt(W)/m, the amplitude rateda/dzitself. The crop scalen_t / n_tothatresponses.prefactorcarries is a dimensionless grid convention and changes the number but not the unit.- Parameters:
- statenumpy.ndarray
The lab-frame channel spectra, complex128, shape
(n_modes, n_pol, n_omega), in the square root of watts, withn_polone.- mapsGeneralMaps
The guide’s synthesis, projection, per-node tensor and frequency factor.
- crop_indexnumpy.ndarray
Where each coarse bin sits in the fine spectrum,
numpy.intp, shape(n_omega,).- n_toint
Fine time samples, positive and even.
- pad_scalefloat
n_to / n_t, the oversampling scale ofconventions.mdsection 3.- transform_weightsnumpy.ndarray or None, optional
The stage bundle’s weights of a moving guide’s maps (
GeneralMaps.at());Nonefor maps that do not move.- dispersive_densitiessequence of float, optional
Each dispersive channel’s number density at the stage, m^-3, in the channels’ order: the stage bundle’s
dispersive_kerr_densities.
- Returns:
- numpy.ndarray
The modal source on the coarse grid, complex128, shape
(n_modes, n_pol, n_omega), in sqrt(W)/m before the crop scale.
- Raises:
- BackendError
If the densities do not match the dispersive channels.
- solprop.backends.numpy_ref.general_modal.node_delayed_maps(tables, regional)[source]#
Derive the delayed response’s per-node ownership, tensors and kernel spectra.
A delayed response has memory, so unlike the instantaneous tensor it cannot be summed into one per-node array: two regions may declare different kernels and a convolution is not linear in the kernel’s owner. What is derived instead is the response each node belongs to, each response’s
chi3_r D[term], and each kernel’s transform on the doubled fine axis, already scaled by the temporal quadrature weight so that the transform product is the causal integral.- Parameters:
- tablessolprop.translate.modal_tables.ModalTransformTables
The guide’s reference, whose
region_indexowns each node’s region.- regionalsolprop.translate.responses.RegionalResponseTables or None
The compiled per-region responses, or
Nonefor a linear guide.
- Returns:
- tuple
The per-node response index (
-1where a node’s region declares none), the scaled delayed tensors and the kernel spectra, the last twoNonewhen no region declares a delayed response.
- Raises:
- BackendError
Naming the leaf and what about it is not executed.
- solprop.backends.numpy_ref.general_modal.node_kerr_form(regional)[source]#
Return which cubic a compiled per-region record declares.
The form is a physical-model choice and travels in
model_id, which is a representation tag inside the resume identity already; it is not a leaf field, because adding one would move a contract row and bump the kernel schema for a string the record can carry.- Parameters:
- regionalsolprop.translate.responses.RegionalResponseTables or None
The compiled per-region responses, or
Nonefor a linear guide, whose zero tensor makes the form immaterial.
- Returns:
- {‘analytic’, ‘real’}
The declared form;
"analytic"for a linear guide.
- Raises:
- BackendError
If the record declares a model identity this backend does not execute.
- solprop.backends.numpy_ref.general_modal.node_kerr_tensor(tables, regional)[source]#
Sum each node’s material responses into one susceptibility tensor.
A response acts in one material region and a node belongs to exactly one, so the tensor a node sees is
sum_r chi3_r T_rover the responses of its region. Two responses in one region add, which is what superposed instantaneous susceptibilities do; a region no response names contributes nothing, which is how a linear cladding is declared.chi3is stored as Chebyshev coefficients along z. This route propagates constant geometry, so one interval of degree zero is required and the single coefficient is the value.- Parameters:
- tablessolprop.translate.modal_tables.ModalTransformTables
The guide’s reference, whose
region_indexowns each node’s region.- regionalsolprop.translate.responses.RegionalResponseTables or None
The compiled per-region responses, or
Nonefor a linear guide.
- Returns:
- numpy.ndarray
The per-node tensor, float64, shape
(n_nodes, 3, 3, 3, 3), in m²/V².
- Raises:
- BackendError
If the responses do not resolve the reference’s components, the susceptibility varies along z, or a delayed term is declared.
- solprop.backends.numpy_ref.general_modal.refuse_regional_responses(tables, regional, *, backend='numpy_ref')[source]#
Refuse a per-region response this route does not execute, before it allocates.
Called from the backend’s admission list, where it runs before the driver’s buffers exist, and again from
node_kerr_tensor(), which is public and must not build a tensor from a record it cannot read. Neither call allocates.A component count that disagrees with the reference is the dangerous one: a one-component tensor broadcasts into the three-component contraction and propagates a meaningless polarisation, and a two-component one dies inside
einsumwith a shape message that names no leaf.- Parameters:
- tablessolprop.translate.modal_tables.ModalTransformTables
The guide’s reference, which fixes the component count.
- regionalsolprop.translate.responses.RegionalResponseTables or None
The compiled per-region responses, or
Nonefor a linear guide.- backendstr, optional
The registry name each refusal names:
numba_cpuexecutes the same responses and runs this same list.
- Raises:
- BackendError
Naming the leaf and what about it is not executed.
- solprop.backends.numpy_ref.general_modal.symmetric_cubic(coefficients, field)[source]#
Return the real cubic
sum_jkl A_ijkl E_j E_k E_lby its symmetric form.The reduction order of the real cubic on every backend: each sample forms the pair products
E_j E_k(j <= k), each monomial as(E_j E_k) E_l, and each component assum_m C_im monomial_mover the monomials in their order, sequential from zero. Against the densesum_j (sum_kl A_ijkl E_k E_l) E_jthat is 3 + 10 products a component in place of 81 + 3.Rounding. With
M_i = sum_jkl |A_ijkl| |E_j| |E_k| |E_l|: a coefficient is withingamma_5of its exact sum, a monomial withingamma_2, and the ten-term sum of products withingamma_10, so the component is withingamma_17 M_iof exact arithmetic; the dense order is withingamma_14(the pair, nine products, the field and three terms), so the two orders differ by at mostgamma_31 M_i.- Parameters:
- coefficientsnumpy.ndarray
symmetric_kerr_coefficients()of the nodes’ tensors, in m²/V².- fieldnumpy.ndarray
The real field at the nodes, float64, shape
(n_nodes, n_components, n_to), in V/m.
- Returns:
- numpy.ndarray
The cubic before
eps0, float64, shape(n_nodes, n_components, n_to), in V/m.
- solprop.backends.numpy_ref.general_modal.symmetric_kerr_coefficients(tensor)[source]#
Sum a per-node tensor over the orderings of each monomial of the real cubic.
On a real field
sum_jkl A_ijkl E_j E_k E_l = sum_m C_im (E_j E_k) E_lover the monomials ofsymmetric_monomials(), withC_imthe sum ofA_ijklover every ordered triple that sorts to monomialm: one ordering forx x x, three forx x y, six forx y z. That is an identity for any tensor, symmetric or not. The orderings are added in lexicographic order of(j, k, l), each sum sequential from zero, which is the order every backend forms them in; a coefficient is withingamma_5of the exact sum relative to the sum of its terms’ magnitudes.- Parameters:
- tensornumpy.ndarray
The per-node
chi3 T, float64, shape(n_nodes, n_components, n_components, n_components, n_components), in m²/V².
- Returns:
- numpy.ndarray
The coefficients, float64, shape
(n_nodes, n_components, n_monomials), in m²/V².
Examples
>>> from solprop.backends.numpy_ref.general_modal import ( ... symmetric_kerr_coefficients, ... ) >>> from solprop.translate.responses import isotropic_kerr_tensor >>> coefficients = symmetric_kerr_coefficients(isotropic_kerr_tensor("real")[None]) >>> coefficients[0, 0, :4] # x: xxx, xxy, xxz, xyy array([1., 0., 0., 1.])
- solprop.backends.numpy_ref.general_modal.symmetric_monomials(n_components)[source]#
Return the real cubic’s monomials
(j, k, l),j <= k <= l, in order.The order is lexicographic, and it is the order
symmetric_cubic()sums them in on every backend: ten for three components, four for two, one for one.- Parameters:
- n_componentsint
Cartesian components of the local field, one to three.
- Returns:
- tuple
The component triples.
Examples
>>> from solprop.backends.numpy_ref.general_modal import symmetric_monomials >>> symmetric_monomials(2) ((0, 0, 0), (0, 0, 1), (0, 1, 1), (1, 1, 1))