Forcings .bc file¶
The forcings .bc files contain forcing data for point locations, for example time series input for a boundary condition. Various quantities and function types are supported.
The forcings file is represented by the classes below.
Model¶
Representation of a .bc file in various classes.
Most relevant classes are:
- ForcingModel: toplevel class containing the whole .bc file contents.
- ForcingBase subclasses: containing the actual data columns, for example: TimeSeries, HarmonicComponent, AstronomicComponent, HarmonicCorrection, AstronomicCorrection, Constant, T3D.
Astronomic (ForcingBase)
pydantic-model
¶
Subclass for a .bc file [Forcing] block with astronomic components data.
AstronomicCorrection (ForcingBase)
pydantic-model
¶
Subclass for a .bc file [Forcing] block with astronomic components correction data.
Constant (ForcingBase)
pydantic-model
¶
Subclass for a .bc file [Forcing] block with constant value data.
ForcingBase (DataBlockINIBasedModel)
pydantic-model
¶
The base class of a single [Forcing] block in a .bc forcings file.
Typically subclassed, for the specific types of forcing data, e.g, TimeSeries.
This model is for example referenced under a
ForcingModel.forcing[..]
.
__repr__(self) -> str
special
¶
Return repr(self).
Source code in hydrolib/core/io/bc/models.py
def __repr__(self) -> str:
data = dict(self)
data["datablock"] = "<omitted>"
representable = BaseModel.construct(**data)
return str(representable)
validate(v)
classmethod
¶
Try to initialize subclass based on the function
field.
This field is compared to each function
field of the derived models of ForcingBase
.
The derived model with an equal function type will be initialized.
Exceptions:
Type | Description |
---|---|
ValueError |
When the given type is not a known structure type. |
Source code in hydrolib/core/io/bc/models.py
@classmethod
def validate(cls, v):
"""Try to initialize subclass based on the `function` field.
This field is compared to each `function` field of the derived models of `ForcingBase`.
The derived model with an equal function type will be initialized.
Raises:
ValueError: When the given type is not a known structure type.
"""
# should be replaced by discriminated unions once merged
# https://github.com/samuelcolvin/pydantic/pull/2336
if isinstance(v, dict):
for c in cls.__subclasses__():
if (
c.__fields__.get("function").default.lower()
== v.get("function", "").lower()
):
v = c(**v)
break
else:
raise ValueError(
f"Function of {cls.__name__} with name={v.get('name', '')} and function={v.get('function', '')} is not recognized."
)
return v
ForcingData
¶
Data type that selects from three different types of forcing data: * a scalar float constant * "realtime" keyword, indicating externally controlled. * A ForcingModel coming from a .bc file.
ForcingGeneral (INIGeneral)
pydantic-model
¶
[General]
section with .bc file metadata.
ForcingModel (INIModel)
pydantic-model
¶
The overall model that contains the contents of one .bc forcings file.
This model is for example referenced under a
ExtModel.boundary[..].forcingfile[..]
.
Harmonic (ForcingBase)
pydantic-model
¶
Subclass for a .bc file [Forcing] block with harmonic components data.
HarmonicCorrection (ForcingBase)
pydantic-model
¶
Subclass for a .bc file [Forcing] block with harmonic components correction data.
QHTable (ForcingBase)
pydantic-model
¶
Subclass for a .bc file [Forcing] block with Q-h table data.
QuantityUnitPair (BaseModel)
pydantic-model
¶
A .bc file header lines tuple containing a quantity name, its unit and optionally a vertical position index.
RealTime (str, Enum)
¶
Enum class containing the valid value for the "realtime" reserved keyword for real-time controlled forcing data, e.g., for hydraulic structures.
This class is used inside the ForcingData Union, to force detection of the realtime keyword, prior to considering it a filename.
realtime
¶
str: Realtime data source, externally provided
T3D (ForcingBase)
pydantic-model
¶
Subclass for a .bc file [Forcing] block with 3D timeseries data.
TimeInterpolation (str, Enum)
¶
Enum class containing the valid values for the time interpolation.
TimeSeries (ForcingBase)
pydantic-model
¶
Subclass for a .bc file [Forcing] block with timeseries data.
VectorQUPValidation
¶
Helper class with validator routines for VectorQuantityUnitPairs objects. To be called from other model classes.
process_vectordefinition_or_check_quantityunitpairs(vectordefs: Optional[List[str]], quantityunitpairs: List[Union[hydrolib.core.io.bc.models.QuantityUnitPair, hydrolib.core.io.bc.models.VectorQuantityUnitPairs]], number_of_element_repetitions: int = 1) -> None
staticmethod
¶
Processes the given vector definition header lines from a .bc file or, if absent, checks whether the existing VectorQuantityUnitPairs objects already have the correct vector length.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
vectordefs |
List[str] |
List of vector definition values, e.g., ["vectorname:comp1,comp2,..compN", ...] |
required |
quantityunitpairs |
List[ScalarOrVectorQUP] |
list of already parsed and constructed QuantityUnitPair objects, which may be modified in place with some packed VectorQuantityUnitPairs objects. |
required |
number_of_element_repetitions |
int |
Number of times each vector element is expected to be present in the subsequent Quantity lines. Typically used for 3D quantities, using the number of vertical layers. Defaults to 1. |
1 |
Source code in hydrolib/core/io/bc/models.py
@staticmethod
def process_vectordefinition_or_check_quantityunitpairs(
vectordefs: Optional[List[str]],
quantityunitpairs: List[ScalarOrVectorQUP],
number_of_element_repetitions: int = 1,
) -> None:
"""Processes the given vector definition header lines from a .bc file
or, if absent, checks whether the existing VectorQuantityUnitPairs
objects already have the correct vector length.
Args:
vectordefs (List[str]): List of vector definition values, e.g.,
["vectorname:comp1,comp2,..compN", ...]
quantityunitpairs (List[ScalarOrVectorQUP]): list of already parsed
and constructed QuantityUnitPair objects, which may be modified
in place with some packed VectorQuantityUnitPairs objects.
number_of_element_repetitions (int, optional): Number of times each
vector element is expected to be present in the subsequent
Quantity lines. Typically used for 3D quantities, using the
number of vertical layers. Defaults to 1."""
if vectordefs is not None and not any(
map(lambda qup: isinstance(qup, VectorQuantityUnitPairs), quantityunitpairs)
):
# Vector definition line still must be processed and VectorQUPs still created.
VectorQUPValidation.validate_vectordefinition_and_update_quantityunitpairs(
vectordefs, quantityunitpairs, number_of_element_repetitions
)
else:
# VectorQUPs already present; directly validate their vector length.
for qup in quantityunitpairs:
if isinstance(qup, VectorQuantityUnitPairs):
VectorQUPValidation.validate_vectorlength(
qup, number_of_element_repetitions
)
validate_vectordefinition_and_update_quantityunitpairs(vectordefs: Optional[List[str]], quantityunitpairs: List[Union[hydrolib.core.io.bc.models.QuantityUnitPair, hydrolib.core.io.bc.models.VectorQuantityUnitPairs]], number_of_element_repetitions: int = 1) -> None
staticmethod
¶
Validates the given vector definition header lines from a .bc file for a ForcingBase subclass and updates the existing QuantityUnitPair list by packing the vector elements into a VectorQuantityUnitPairs object for each vector definition.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
vectordefs |
List[str] |
List of vector definition values, e.g., ["vectorname:comp1,comp2,..compN", ...] |
required |
quantityunitpairs |
List[ScalarOrVectorQUP] |
list of already parsed and constructed QuantityUnitPair objects, which will be modified in place with some packed VectorQuantityUnitPairs objects. |
required |
number_of_element_repetitions |
int |
Number of times each vector element is expected to be present in the subsequent Quantity lines. Typically used for 3D quantities, using the number of vertical layers. Defaults to 1. |
1 |
Source code in hydrolib/core/io/bc/models.py
@staticmethod
def validate_vectordefinition_and_update_quantityunitpairs(
vectordefs: Optional[List[str]],
quantityunitpairs: List[ScalarOrVectorQUP],
number_of_element_repetitions: int = 1,
) -> None:
"""Validates the given vector definition header lines from a .bc file
for a ForcingBase subclass and updates the existing QuantityUnitPair list
by packing the vector elements into a VectorQuantityUnitPairs object
for each vector definition.
Args:
vectordefs (List[str]): List of vector definition values, e.g.,
["vectorname:comp1,comp2,..compN", ...]
quantityunitpairs (List[ScalarOrVectorQUP]): list of already parsed
and constructed QuantityUnitPair objects, which will be modified
in place with some packed VectorQuantityUnitPairs objects.
number_of_element_repetitions (int, optional): Number of times each
vector element is expected to be present in the subsequent
Quantity lines. Typically used for 3D quantities, using the
number of vertical layers. Defaults to 1."""
if vectordefs is None:
return
vectordefs = to_list(vectordefs)
qup_iter = iter(quantityunitpairs)
# Start a new list, to only keep the scalar QUPs, and add newly
# created VectorQUPs.
quantityunitpairs_with_vectors = []
# If one quantity is "time", it must be the first one.
if quantityunitpairs[0].quantity == "time":
quantityunitpairs_with_vectors.append(quantityunitpairs[0])
_ = next(qup_iter)
# For each vector definition line, greedily find the quantity unit pairs
# that form the vector elements, and pack them into a single VectorQuantityUnitPairs oject.
for vectordef in vectordefs:
vectorname, componentdefs = vectordef.split(":")
componentnames = re.split(r"[, \t]", componentdefs)
n_components = len(componentnames)
vqu_pair = VectorQuantityUnitPairs(
vectorname=vectorname, elementname=componentnames, quantityunitpair=[]
)
n_rep = 0
for qu_pair in qup_iter:
if qu_pair.quantity in componentnames:
# This vector element found, store it.
vqu_pair.quantityunitpair.append(qu_pair)
n_rep += 1
if n_rep == n_components * number_of_element_repetitions:
break
else:
# This quantity was no vector element being searched for
# so keep it as a regular (scalar) QuantityUnitPair.
quantityunitpairs_with_vectors.append(qu_pair)
if VectorQUPValidation.validate_vectorlength(
vqu_pair, number_of_element_repetitions
):
# This VectorQuantityUnitPairs is now complete; add it to result list.
quantityunitpairs_with_vectors.append(vqu_pair)
quantityunitpairs[:] = quantityunitpairs_with_vectors
validate_vectorlength(vqu_pair: VectorQuantityUnitPairs, number_of_element_repetitions: int = 1) -> bool
staticmethod
¶
Checks whether the number of QuantityUnitPairs in a vector quantity matches exactly with number of vector elements in the definition and, optionally, the number of vertical layers.
Parameters:
Name | Type | Description | Default |
---|---|---|---|
vqu_pair |
VectorQuantityUnitPairs |
the vector quantity object to be checked. |
required |
number_of_element_repetitions |
int |
Number of times each vector element is expected to be present in the subsequent Quantity lines. Typically used for 3D quantities, using the number of vertical layers. Defaults to 1. |
1 |
Returns:
Type | Description |
---|---|
bool |
True if vqu_pair is valid. False return value is hidden because an exception will be raised. |
Exceptions:
Type | Description |
---|---|
ValueError |
If number of QuantityUnitPair objects in vqu_pair is not equal to number of element names * number_of_element_repetitions. |
Source code in hydrolib/core/io/bc/models.py
@staticmethod
def validate_vectorlength(
vqu_pair: VectorQuantityUnitPairs,
number_of_element_repetitions: int = 1,
) -> bool:
"""Checks whether the number of QuantityUnitPairs in a vector quantity
matches exactly with number of vector elements in the definition and,
optionally, the number of vertical layers.
Args:
vqu_pair (VectorQuantityUnitPairs): the vector quantity object to be checked.
number_of_element_repetitions (int, optional): Number of times each
vector element is expected to be present in the subsequent
Quantity lines. Typically used for 3D quantities, using the
number of vertical layers. Defaults to 1.
Returns:
bool: True if vqu_pair is valid. False return value is hidden because
an exception will be raised.
Raises:
ValueError: If number of QuantityUnitPair objects in vqu_pair is not equal
to number of element names * number_of_element_repetitions."""
if not (
valid := len(vqu_pair.quantityunitpair)
== len(vqu_pair.elementname) * number_of_element_repetitions
):
raise ValueError(
f"Incorrect number of quantity unit pairs were found; should match the elements in vectordefinition for {vqu_pair.vectorname}"
+ (
f", and {number_of_element_repetitions} vertical layers"
if number_of_element_repetitions > 1
else ""
)
+ "."
)
return valid
VectorQuantityUnitPairs (BaseModel)
pydantic-model
¶
A subset of .bc file header lines containing a vector quantity definition, followed by all component quantity names, their unit and optionally their vertical position indexes.
__str__(self) -> str
special
¶
Return str(self).
Source code in hydrolib/core/io/bc/models.py
def __str__(self) -> str:
return VectorQuantityUnitPairs._to_vectordefinition_string(
self.vectorname, self.elementname
)
VerticalInterpolation (str, Enum)
¶
Enum class containing the valid values for the vertical position type, which defines what the numeric values for vertical position specification mean.
VerticalPositionType (str, Enum)
¶
Enum class containing the valid values for the vertical position type.