ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
erf_sbm::SBMLayout Class Reference

#include <ERF_SBMLayout.H>

Collaboration diagram for erf_sbm::SBMLayout:

Public Member Functions

 SBMLayout (SBMLayoutSpec spec)
 
const std::vector< PopulationLayout > & populations () const noexcept
 
const std::vector< AttachedPropertyDescriptor > & attached_properties () const noexcept
 
const ::erf_auxiliary::AuxiliaryProjection & bulk_projection () const noexcept
 
const LiquidProjectionSpec & liquid_projection () const noexcept
 
int ncomp () const noexcept
 
int mass_offset (const int population) const
 
int property_offset (const int property) const
 
std::string schema_identity () const noexcept
 
std::string inspection () const
 
::erf_auxiliary::AuxiliaryStateLayout auxiliary_layout () const
 

Static Public Member Functions

static LayoutValidation validate (const SBMLayoutSpec &spec)
 

Private Attributes

std::vector< PopulationLayout > m_populations
 
std::vector< AttachedPropertyDescriptor > m_properties
 
std::vector< int > m_property_offsets
 
LiquidProjectionSpec m_liquid_projection
 
::erf_auxiliary::AuxiliaryProjection m_projection
 
int m_ncomp {0}
 
std::string m_schema_identity
 

Detailed Description

Immutable runtime SBM component and projection layout.

Constructor & Destructor Documentation

◆ SBMLayout()

erf_sbm::SBMLayout::SBMLayout ( SBMLayoutSpec  spec)
explicit
83 {
84  const auto result = validate(spec);
85  if (!result.valid) throw std::invalid_argument("invalid SBM layout: " + result.message);
86 
87  // Input order is part of the deterministic schema. Offsets are assigned
88  // once and never changed after construction.
89  int offset = 0;
90  for (std::size_t i = 0; i < spec.populations.size(); ++i) {
91  const auto& input = spec.populations[i];
92  PopulationLayout population{input.population_id, input.semantic_id, input.phase,
93  SpectralGrid(input.grid), input.moment_mode,
94  input.mass_state_units, input.number_state_units,
95  offset, -1, 0, {}};
96  population.component_count = population.grid.nbins();
97  offset += population.component_count;
98  if (population.moment_mode == MomentMode::TwoMoment) {
99  population.number_offset = offset;
100  population.component_count += population.grid.nbins();
101  offset += population.grid.nbins();
102  }
103  m_populations.push_back(std::move(population));
104  }
105  for (const auto& property : spec.attached_properties) {
106  m_properties.push_back(property);
107  auto carrier = std::find_if(m_populations.begin(), m_populations.end(),
108  [&](const PopulationLayout& candidate) {
109  return candidate.population_id == m_properties.back().carrier_population;
110  });
111  if (carrier == m_populations.end()) {
112  throw std::invalid_argument("attached property refers to an unknown population");
113  }
114  // An attached extensive property is represented per carrier bin. A
115  // number-carried property is not folded into the number block and a
116  // mass-bounded subset is not folded into liquid mass; both remain
117  // typed, independently addressable transport components.
118  m_property_offsets.push_back(offset);
119  carrier->property_component_offsets.push_back(offset);
120  offset += carrier->grid.nbins();
121  }
122  m_ncomp = offset;
123 
124  std::vector<::erf_auxiliary::ProjectionRule> rules;
125  m_liquid_projection = spec.liquid_projection;
126  const auto liquid = std::find_if(m_populations.begin(), m_populations.end(),
127  [&](const PopulationLayout& population) {
128  return population.population_id == m_liquid_projection.population_id;
129  });
130  const int split = m_liquid_projection.cloud_rain_split;
131  rules.push_back({"qc", liquid->mass_offset, split});
132  rules.push_back({"qr", liquid->mass_offset + split, liquid->grid.nbins() - split});
135  throw std::invalid_argument("SBM projection does not match layout");
136  }
137 
138  std::ostringstream schema;
139  schema << "sbm-layout-v1|ncomp=" << m_ncomp
140  << "|constraint_policy=nonnegative-bin-mass-and-moments-v1"
141  << "|projection=liquid-mass-sum-to-qc-qr-v1"
142  << "|projection_population=" << m_liquid_projection.population_id
143  << "|projection_cloud_rain_split=" << m_liquid_projection.cloud_rain_split << '|';
144  for (const auto& p : m_populations) {
145  const auto identity = representation_identity(p.moment_mode);
146  schema << "population=" << p.population_id << ':' << p.semantic_id
147  << ":phase=" << static_cast<int>(p.phase) << ':' << p.grid.identity()
148  << ":moment=" << static_cast<int>(p.moment_mode)
149  << ":representation=" << identity.representation
150  << ":reconstruction=" << identity.reconstruction
151  << ":packet_remap=" << identity.packet_remap
152  << ":mass_units=" << p.mass_state_units
153  << ":number_units=" << p.number_state_units
154  << ":mass=" << p.mass_offset << ":number=" << p.number_offset << '|';
155  }
156  for (std::size_t i = 0; i < m_properties.size(); ++i) {
157  const auto& p = m_properties[i];
158  schema << "property=" << p.name << ':' << p.semantic_id << ':' << p.units
159  << ':' << p.carrier_population << ':' << static_cast<int>(p.kind)
160  << ":remap=" << static_cast<int>(p.remap_policy)
161  << ":support=" << static_cast<int>(p.support)
162  << ":transported=" << (p.transported ? 1 : 0)
163  << ":may_overlap_mass=" << (p.may_overlap_mass ? 1 : 0)
164  << ":support_min=" << canonical_real(p.support_min)
165  << ":support_max=" << canonical_real(p.support_max, true)
166  << ":offset=" << m_property_offsets[i] << '|';
167  }
168  m_schema_identity = schema.str();
169 }
AMREX_FORCE_INLINE IntVect offset(const int face_dir, const int normal)
Definition: ERF_ReadBndryPlanes.cpp:32
Definition: ERF_AuxiliaryProjection.H:28
ProjectionValidation validate(const int source_components) const
Definition: ERF_AuxiliaryProjection.cpp:7
std::vector< int > m_property_offsets
Definition: ERF_SBMLayout.H:113
LiquidProjectionSpec m_liquid_projection
Definition: ERF_SBMLayout.H:114
int m_ncomp
Definition: ERF_SBMLayout.H:116
std::string m_schema_identity
Definition: ERF_SBMLayout.H:117
std::vector< AttachedPropertyDescriptor > m_properties
Definition: ERF_SBMLayout.H:112
std::vector< PopulationLayout > m_populations
Definition: ERF_SBMLayout.H:111
::erf_auxiliary::AuxiliaryProjection m_projection
Definition: ERF_SBMLayout.H:115
static LayoutValidation validate(const SBMLayoutSpec &spec)
Definition: ERF_SBMLayout.cpp:12
@ p
Definition: ERF_WSM6.H:280
constexpr RepresentationIdentity representation_identity(const MomentMode mode) noexcept
Definition: ERF_SBMRepresentation.H:16
std::string canonical_real(const amrex::Real value, const bool allow_unbounded_nan=false)
Definition: ERF_SBMCanonicalIdentity.H:18
bool valid
Definition: ERF_AuxiliaryProjection.H:23
int population_id
Definition: ERF_SBMLayout.H:41
int cloud_rain_split
Definition: ERF_SBMLayout.H:42
Here is the call graph for this function:

Member Function Documentation

◆ attached_properties()

const std::vector<AttachedPropertyDescriptor>& erf_sbm::SBMLayout::attached_properties ( ) const
inlinenoexcept
100 { return m_properties; }

Referenced by erf_sbm::make_attached_property_support_descriptors(), and erf_sbm::make_constraint_groups().

Here is the caller graph for this function:

◆ auxiliary_layout()

erf_auxiliary::AuxiliaryStateLayout erf_sbm::SBMLayout::auxiliary_layout ( ) const
221 {
222  std::vector<::erf_auxiliary::ComponentDescriptor> components;
223  components.reserve(static_cast<std::size_t>(m_ncomp));
224  for (const auto& p : m_populations) {
225  for (int b = 0; b < p.grid.nbins(); ++b) {
226  components.push_back({"population" + std::to_string(p.population_id) + ".mass." + std::to_string(b),
227  p.semantic_id + ".mass", p.mass_state_units});
228  }
229  if (p.number_offset >= 0) {
230  for (int b = 0; b < p.grid.nbins(); ++b) {
231  components.push_back({"population" + std::to_string(p.population_id) + ".number." + std::to_string(b),
232  p.semantic_id + ".number", p.number_state_units});
233  }
234  }
235  }
236  for (std::size_t i = 0; i < m_properties.size(); ++i) {
237  const auto& property = m_properties[i];
238  const auto& carrier = std::find_if(m_populations.begin(), m_populations.end(),
239  [&](const PopulationLayout& candidate) {
240  return candidate.population_id == property.carrier_population;
241  });
242  for (int b = 0; b < carrier->grid.nbins(); ++b) {
243  components.push_back({property.name + "." + std::to_string(b),
244  property.semantic_id, property.units});
245  }
246  }
247  return {m_schema_identity, std::move(components)};
248 }

◆ bulk_projection()

const ::erf_auxiliary::AuxiliaryProjection& erf_sbm::SBMLayout::bulk_projection ( ) const
inlinenoexcept
101 { return m_projection; }

◆ inspection()

std::string erf_sbm::SBMLayout::inspection ( ) const
186 {
187  std::ostringstream out;
188  out << "schema=" << m_schema_identity << "\ncomponents=" << m_ncomp << "\n";
189  for (const auto& p : m_populations) {
190  const auto identity = representation_identity(p.moment_mode);
191  out << "population " << p.population_id << " bins=" << p.grid.nbins()
192  << " semantic_id=" << p.semantic_id
193  << " moment_mode=" << static_cast<int>(p.moment_mode)
194  << " representation=" << identity.representation
195  << " reconstruction=" << identity.reconstruction
196  << " packet_remap=" << identity.packet_remap
197  << " coordinate_units=" << p.grid.coordinate_units()
198  << " mass_state_units=" << p.mass_state_units
199  << " number_state_units=" << p.number_state_units
200  << " mass_offset=" << p.mass_offset
201  << " number_offset=" << p.number_offset << "\n";
202  }
203  const auto liquid = std::find_if(m_populations.begin(), m_populations.end(),
204  [&](const PopulationLayout& population) {
205  return population.population_id == m_liquid_projection.population_id;
206  });
207  out << "projection qc=population" << liquid->population_id << "[0:" << m_liquid_projection.cloud_rain_split
208  << "] qr=population" << liquid->population_id << "[" << m_liquid_projection.cloud_rain_split << ':'
209  << liquid->grid.nbins() << "]\n";
210  for (std::size_t i = 0; i < m_properties.size(); ++i) {
211  out << "property " << m_properties[i].name
212  << " kind=" << static_cast<int>(m_properties[i].kind)
213  << " remap=" << static_cast<int>(m_properties[i].remap_policy)
214  << " carrier=" << m_properties[i].carrier_population
215  << " offset=" << m_property_offsets[i] << '\n';
216  }
217  return out.str();
218 }
Here is the call graph for this function:

◆ liquid_projection()

const LiquidProjectionSpec& erf_sbm::SBMLayout::liquid_projection ( ) const
inlinenoexcept
102 { return m_liquid_projection; }

Referenced by erf_sbm::SBMTransport::advance_stage(), and erf_sbm::SBMBulkProjection::SBMBulkProjection().

Here is the caller graph for this function:

◆ mass_offset()

int erf_sbm::SBMLayout::mass_offset ( const int  population) const
180 {
181  for (const auto& p : m_populations) if (p.population_id == population) return p.mass_offset;
182  throw std::out_of_range("unknown SBM population");
183 }

Referenced by erf_sbm::SBMTransport::advance_stage().

Here is the caller graph for this function:

◆ ncomp()

◆ populations()

◆ property_offset()

int erf_sbm::SBMLayout::property_offset ( const int  property) const
172 {
173  if (property < 0 || property >= static_cast<int>(m_property_offsets.size())) {
174  throw std::out_of_range("unknown SBM attached property");
175  }
176  return m_property_offsets[static_cast<std::size_t>(property)];
177 }

Referenced by erf_sbm::make_attached_property_support_descriptors(), and erf_sbm::make_constraint_groups().

Here is the caller graph for this function:

◆ schema_identity()

std::string erf_sbm::SBMLayout::schema_identity ( ) const
inlinenoexcept
106 { return m_schema_identity; }

Referenced by erf_sbm::restart_schema().

Here is the caller graph for this function:

◆ validate()

LayoutValidation erf_sbm::SBMLayout::validate ( const SBMLayoutSpec &  spec)
static
13 {
14  if (spec.populations.empty()) return {false, "at least one SBM population is required"};
15  std::vector<int> ids;
16  for (std::size_t i = 0; i < spec.populations.size(); ++i) {
17  const auto& population = spec.populations[i];
18  const auto grid_result = SpectralGrid::validate(population.grid);
19  if (!grid_result.valid) return {false, grid_result.message};
20  if (population.grid.coordinate_kind != CoordinateKind::Mass) {
21  return {false, "reference SBM representations require individual-particle-mass coordinates"};
22  }
23  if (population.moment_mode != MomentMode::OneMoment &&
24  population.moment_mode != MomentMode::TwoMoment) {
25  return {false, "population moment mode is unsupported"};
26  }
27  if (population.population_id < 0 || population.semantic_id.empty() ||
28  population.mass_state_units.empty() || population.number_state_units.empty()) {
29  return {false, "population id, semantic id, and state units are required"};
30  }
31  if (population.moment_mode == MomentMode::OneMoment) {
32  for (std::size_t bin = 1; bin < population.grid.pivots.size(); ++bin) {
33  if (!(population.grid.pivots[bin - 1] < population.grid.pivots[bin])) {
34  return {false, "one-moment fixed pivots must increase strictly across adjacent bins"};
35  }
36  }
37  }
38  if (std::find(ids.begin(), ids.end(), population.population_id) != ids.end()) {
39  return {false, "population ids must be unique"};
40  }
41  ids.push_back(population.population_id);
42  }
43  const auto liquid = std::find_if(spec.populations.begin(), spec.populations.end(),
44  [&](const SpectralPopulationSpec& population) {
45  return population.population_id == spec.liquid_projection.population_id;
46  });
47  if (liquid == spec.populations.end()) return {false, "liquid projection refers to an unknown population"};
48  if (liquid->phase != PopulationPhase::Liquid) {
49  return {false, "bulk cloud/rain projection must refer to a liquid population"};
50  }
51  if (spec.liquid_projection.cloud_rain_split <= 0 ||
52  spec.liquid_projection.cloud_rain_split >= static_cast<int>(liquid->grid.edges.size()) - 1) {
53  return {false, "cloud/rain split must be an interior liquid-population bin index"};
54  }
55  for (const auto& property : spec.attached_properties) {
56  if (property.name.empty() || property.semantic_id.empty() || property.units.empty()) {
57  return {false, "attached properties need name, semantic id, and units"};
58  }
59  if (std::find(ids.begin(), ids.end(), property.carrier_population) == ids.end()) {
60  return {false, "attached property refers to an unknown population"};
61  }
62  if (property.may_overlap_mass && property.kind == PropertyKind::MassBoundedSubset) {
63  return {false, "mass-bounded subset cannot overlap liquid mass"};
64  }
65  if (property.remap_policy != PropertyRemapPolicy::CarrierBinConservative) {
66  return {false, "P1 attached properties require carrier-bin conservative remapping"};
67  }
68  if (!property.transported) {
69  return {false, "P1 attached properties must be transported when registered"};
70  }
71  if (!std::isfinite(property.support_min) || property.support_min < amrex::Real(0.0)) {
72  return {false, "attached-property support_min must be finite and nonnegative"};
73  }
74  if (!std::isnan(property.support_max) &&
75  (!std::isfinite(property.support_max) || property.support_max < property.support_min)) {
76  return {false, "attached-property support_max must be NaN or finite and >= support_min"};
77  }
78  }
79  return {true, {}};
80 }
amrex::Real Real
Definition: ERF_ShocInterface.H:19
static GridValidation validate(const SpectralGridSpec &spec)
Definition: ERF_SpectralGrid.cpp:13

Referenced by SBMLayout().

Here is the call graph for this function:
Here is the caller graph for this function:

Member Data Documentation

◆ m_liquid_projection

LiquidProjectionSpec erf_sbm::SBMLayout::m_liquid_projection
private

◆ m_ncomp

int erf_sbm::SBMLayout::m_ncomp {0}
private

◆ m_populations

std::vector<PopulationLayout> erf_sbm::SBMLayout::m_populations
private

◆ m_projection

::erf_auxiliary::AuxiliaryProjection erf_sbm::SBMLayout::m_projection
private

Referenced by bulk_projection(), and SBMLayout().

◆ m_properties

std::vector<AttachedPropertyDescriptor> erf_sbm::SBMLayout::m_properties
private

◆ m_property_offsets

std::vector<int> erf_sbm::SBMLayout::m_property_offsets
private

◆ m_schema_identity

std::string erf_sbm::SBMLayout::m_schema_identity
private

The documentation for this class was generated from the following files: