"""Write a `SequenceModelGrid` to a file."""from__future__importannotationsimporterrnoimportosfromcollections.abcimportIterablefromosimportPathLikeimportnumpyasnpfromlandlabimportComponentfromsequence.gridimportSequenceModelGridfromsequence.netcdfimportto_netcdf
[docs]classOutputWriter(Component):"""Write output to a netcdf file."""
[docs]def__init__(self,grid:SequenceModelGrid,filepath:str|PathLike[str],interval:int=1,fields:Iterable[str]|None=None,clobber:bool=False,rows:Iterable[str]|None=None,):"""Create an output-file writer. Parameters ---------- grid : SequenceModelGrid The grid to write to a file. filepath : path-like Path to the output file. interval : int, optional The number of time steps between updates to the output file. fields : list of str, optional Names of (at-node) fields to include in the output file. clobber : bool, optional If `True` and the provided file already exists, quietly overwrite it, otherwise raise an exception. rows : iterable of int The rows of the grid to include in the file. """iffieldsisNone:fields=[]super().__init__(grid)self._clobber=clobberself.interval=intervalself.fields=fieldsself.filepath=filepathifrowsisnotNone:self._rows=np.asarray(rows)-1else:self._rows=np.arange(grid.shape[0]-2)self._time=0.0self._step_count=0
[docs]defrun_one_step(self,dt:float|None=None)->None:"""Update the writer by a time step. Parameters ---------- dt : float, optional The time step to update the component by. """dt=1.0ifdtisNoneelsefloat(dt)ifself._step_count%self.interval==0:to_netcdf(self.grid,self.filepath,mode="a",time=self._time,names={"node":self.fields},ids={"row":self._rows,"column":np.arange(self.grid.shape[1]-2),},)self._time+=dtself._step_count+=1
@propertydeffilepath(self)->str|PathLike[str]:"""Return the path to the output file."""returnself._filepath@filepath.setterdeffilepath(self,new_val:str|PathLike[str])->None:ifos.path.isfile(new_val)andnotself._clobber:raiseRuntimeError("file exists")try:os.remove(new_val)exceptOSErroraserr:iferr.errno!=errno.ENOENT:raisefinally:self._filepath=str(new_val)@propertydefinterval(self)->int:"""Return the interval for which output will be written."""returnself._interval@interval.setterdefinterval(self,new_val:int)->None:ifnew_val<0:raiseTypeError("interval not an integer")elifnotisinstance(new_val,int):raiseValueError("non-positive interval")self._interval=new_val@propertydeffields(self)->Iterable[str]:"""Return the names of the fields to include in the output file."""returnself._fields@fields.setterdeffields(self,new_val:Iterable[str])->None:self._fields=tuple(new_val)